Читаем Технологии программирования полностью

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

Общим для них является то, что при их вызове адрес определяется не во время компиляции, а во время выполнения путем поиска в специальных таблицах. Такой способ еще называется поздним связыванием. Разница между методами заключается в особенности поиска адреса.

Когда компилятор встречает обращение к виртуальному методу, он подставляет вместо обращения к конкретному адресу код, который обращается к специальной таблице и извлекает оттуда нужный адрес. Эта таблица называется таблицей виртуальных методов (Virtual Method Table, VMT), и она есть для каждого объектного типа. В ней хранятся адреса всех виртуальных методов класса независимо от того, унаследованы ли они от предка или перекрыты. Отсюда и достоинства, и недостатки виртуальных методов: они вызываются сравнительно быстро (но медленнее статических), однако для хранения указателей на них требуется большое количество памяти.

Динамические методы вызываются медленнее, но позволяют более экономно расходовать память. Каждому динамическому методу системой присваивается уникальный индекс. В таблице динамических методов (Dynamic Method Table, DMT) класса хранятся индексы и адреса только тех динамических методов, которые описаны в данном классе (базовая информация по обработке динамических методов содержится в модуле x: \delphi\source\rtl\sys\dmth.asm). При вызове динамического метода происходит поиск в этой таблице. В случае неудачи просматриваются все классы-предки в порядке иерархии и, наконец, TObject, где имеется стандартный обработчик вызова динамических методов. Экономия памяти налицо.

Для перекрытия и виртуальных, и динамических методов служит новая директива override, с помощью которой (и только с ней!) можно переопределять оба этих типа методов:

type

TFirstClass = class

FMyField1: Integer;

FMyField2: Longlnt;

procedure StatMethod1;

procedure VirtMethod1; virtual;

procedure VirtMethod2; virtual;

procedure DynaMethod1; dynamic;

procedure DynaMethod2; dynamic;

end;

TSecondClass = class(TFirstClass)

procedure StatMethod;

procedure VirtMethod; override;

procedure StatMethod; override;

end;

var

Obj2: TFirstClass;

Obj1: TSecondClass;

Первый из методов в примере создается заново, остальные два — перекрываются. Попытка применить override к статическому методу вызовет ошибку компиляции.

В Object Pascal абстрактными называются методы, которые определены в классе, но не содержат никаких действий, никогда не вызываются и должны быть переопределены в потомках класса. Абстрактными могут быть только виртуальные и динамические методы. Для этого используется директива abstract, указываемая при описании метода:

procedure NeverCallMe; virtual; abstract;

При этом никакого кода для этого метода писать не нужно. Как и ранее, вызов метода NeverCallMe приведет к ошибке времени выполнения.

7. ПОЛИМОРФИЗМ

Рассмотрим пример, которой поясняет, для чего нужно использование абстрактных методов. Пусть у нас имеются некое обобщенное поле для хранения данных — класс TField и три его потомка — для хранения строк, целых и вещественных чисел. В данном случае класс Tfield не используется сам по себе; его основное предназначение — быть родоначальником иерархии конкретных классов — "полей" и дать возможность абстрагироваться от частностей. Хотя параметр у ShowData и описан как TField, но если поместить туда объект этого класса, произойдет ошибка вызова абстрактного метода:

type

TField = class

function GetData: string; virtual; abstract;

end;

TStringField = class(TField)

Data: string;

function GetData: string; override;

end;

TIntegerField = class(TField)

Data: Integer;

function GetData: string; override;

end;

TExtendedField = class(TField)

Data: Integer;

function GetData: string; override;

end;

function TStringField.GetData;

begin

GetData:= Data;

end;

Перейти на страницу:

Похожие книги