Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

04 Лекция - Наследование

.pdf
Скачиваний:
55
Добавлен:
13.04.2015
Размер:
578.51 Кб
Скачать

RTL уже предоставляет несколько базовых классов для реализации базового поведения, требуемого интерфейсом iInterface. Для внутренних объектов используйте класс TInterfacedObject.

Методы интерфейса можно реализовать с помощью статических методов (как в предыдущем примере) или с помощью виртуальных методов. В классах-потомках с помощью директивы override можно подменить виртуальные методы. Если не использовать виртуальные методы, то в классенаследнике все еще можно предоставить новую реализацию, повторно объявив тип интерфейса и повторно выполнив привязку методов интерфейса к новым версиям статических методов. На первый взгляд, использование виртуальных методов для реализации интерфейсов позволяет в классахнаследниках осуществить более «гладкое» кодирование, но оба подхода одинаково мощны и удобны. Однако использование виртуальных методов влияет на объем программного кода и использование памяти.

Для того чтобы скорректировать точки входа вызова интерфейса на соответствующий метод реализации класса, компилятор должен сгенерировать процедуры-«заглушки», а также скорректировать указатель self. «Заглушки» метода интерфейса для статических методов должны корректировать self и «перепрыгнуть» к реальному методу данного класса. «Заглушки» метода интерфейса для виртуальных методов гораздо более сложны и требуют приблизительно в четыре раза больший объем кода (от 20 до 30 байт) по сравнению со статическими «заглушками». К тому же добавление в реализацию класса дополнительных виртуальных методов только увеличивает размер таблицы виртуальных методов (VMT), которая гораздо больше в классе реализации и всех его потомках. Интерфейс уже имеет собственную VMT, а повторное объявление интерфейса в потомках для повторной привязки интерфейса к новым методам столь же полиморфно, как использование виртуальных методов, но требует значительно меньший объем программного кода.

Теперь, после того как определена реализация интерфейса, можно написать программный код, использующий объект этого класса посредством переменной соответствующего типа:

var

Flyer1: ICanFly; begin

Flyer1 := TAirplane.Create; Flyer1.Fly;

end;

Как только вы присвоили объект переменной типа интерфейс, Delphi автоматически с помощью оператора as просматривает, реализует ли объект этот интерфейс. Можно явно выразить это действие следующим образом:

Flyer1 := TAirplane.Create as ICanFly;

Когда оператор as используется в отношении классов или интерфейсов, компилятор генерирует различный код. Для классов компилятор вводит проверки во время выполнения для того, чтобы убедиться, что объект действительно «совместим по типу» с данным классом. Для интерфейсов

компилятор «представляет», что он может извлечь необходимый интерфейс из доступного типа класса. Эта операция подобна «времени компиляции as», а не чему-то, что существует во время выполнения.

Используете ли вы непосредственное присвоение или выражение as, среда Delphi делает одно и то же действие: она вызывает метод _AddRef данного объекта (объявленный iInterface). Стандартная реализация этого метода, как и реализация, предоставляемая TInterfacedObject, приводит к увеличению счетчика ссылок на объект. В то же время, как только переменная Flyer1 выйдет из области действия, Delphi вызывает метод _Release (снова часть Ilnterface). Реализация метода _Release классом TInterfacedObject

уменьшает счетчик ссылок, проверяя, не имеет ли он значение «ноль», и в случае необходимости уничтожает объект. Вот поэтому в предыдущем примере нет программного кода, освобождающего ресурсы, используемые созданным объектом.

Иначе говоря, объекты, на которые ссылаются «интерфейсные» переменные, являются в Delphi переменными с подсчетом ссылок. Эти объекты автоматически высвобождают занимаемую ими память, когда на них нет ссылок «интерфейсных» переменных.

При использовании объектов, основанных на интерфейсах, обращаться к ним можно только с помощью объектных ссылок или только с помощью интерфейсных ссылок. Смешение двух подходов нарушает реализуемую Delphi работу схемы учета ссылок, и может вызвать проблемы использования памяти, которые очень сложно отследить. На практике, если вы решили использовать интерфейсы, необходимо использовать только «интерфейсные» переменные. Если вместо этого вам необходимо смешивать их, то отключите подсчет ссылок, написав вместо TInterfacedObject собственный базовый класс.

Ссылки класса

Последней особенностью, которую мы рассмотрим в этой теме, является использование ссылок класса (class references) которые предполагают идею самостоятельного манипулирования классами с помощью программного кода Первый момент, который необходимо помнить, ссылка класса — это не объект, это ссылка на тип класса. Ссылка класса определяет тип переменной ссылки класса.

Предположим, что вы определили класс TmyClass. Теперь можно определить новый тип ссылки класса, связанной с данным классом:

type

TMyClassRef = class of TMyClass;

А теперь можно определить переменные обоих типов. Первая переменная ссылается на объект, вторая — на класс,

var

AnObject: TMyClass; AClassRef: TMyClassRef; begin

AnObject := TMyClass.Create; AClassRef := TMyClass;

В общем случае ссылки классов позволяют манипулировать типом данных класса в ходе выполнения. Ссылку класса можно использовать в любом выражении, где использование типа данных является законным. Таких выражений не очень много, но некоторые случаи, такие как создание объекта, являются довольно интересными. Последнюю строчку предыдущего примера можно переписать:

AnObject := AClassRef.Create;

На этот раз конструктор Create применяется к ссылке класса, а не к действительному классу; ссылка класса используется для создания объекта этого класса.

Типы ссылок класса не были бы столь полезными, если бы они не поддерживали то же правило совместимости типов, что и типы классов. При объявлении переменной ссылки класса, такой как MyCLassRef, потом ей можно присвоить тот же класс и любой класс-наследник. Поэтому если TMyNewCLass является классом-наследником класса TMyClass, то можно также написать:

AClassRef := TMyNewClass;

Delphi объявляет множество ссылок классов в RTL- и VCL-библиотеках, например.

TClass = class of TObject; TComponentClass = class of TComponent; TFormClass = class of TForm;

В частности, тип ссылки класса TClass может использоваться для хранения ссылки на любой класс, написанный в Delphi, поскольку каждый класс, разумеется, исходит от Tobject. Ссылка TFormClass используется в исходном программном коде большинства Delphi-проектов. Метод CreateForm объекта Application требует в качестве параметра класс создаваемой формы:

Application.CreateForm(TForm1, Form1);

Первым параметром является ссылка класса, вторым — переменная, которая хранит ссылку на создаваемый экземпляр объекта.

И, наконец, имея ссылку класса, можно применить ее к методам связанного класса. Учитывая, что каждый класс является потомком TObject, к каждой ссылке класса можно применить некоторые методы TObject.