Метод становится виртуальным, если за его объявлением в типе
объекта стоит новое зарезервированное слово virtual. Помните, что
если вы объявляете метод в родительском типе как virtual, то все
методы с аналогичными именами в дочерних типах также должны объ-
являться виртуальными во избежание ошибки компилятора.
Ниже приведены знакомые вам объекты из примера платежной ве-
домости, должным образом виртуализированные:
tyрe
PEmрloyee = ^TEmployee;
TEmployee = object
Name, Title: string[25];
Rate: Real;
constructor Init (AName, ATitle: String; ARate: Real);
function GetPayAmount : Real; virtual;
function GetName : String;
function GetTitle : String;
function GetRate : Real;
рrocedure Show; virtual;
end;
PHourly = ^THourly;
THourly = object(TEmployee);
Time: Integer;
constructor Init (AName, ATitle: String; ARate: Real;
Time: Integer);
function GetPayAmount : Real; virtual;
function GetTime : Integer;
end;
PSalaried = ^TSalaried;
TSalaried = object(TEmployee);
function GetPayAmount : Real; virtual;
end;
PCommissioned = ^TCommissioned;
TCommissioned = object(Salaried);
Commission : Real;
SalesAmount : Real;
constructor Init (AName, ATitle: String;
ARate, ACommission, ASalesAmount: Real);
function GetPayAmount : Real; virtual;
end;
А ниже приводится пример для насекомых, дополненный вирту-
альными методами.
tyрe
TWinged = object(Insect)
constructor Init (AX, AY : Integer)
рrocedure Show; virtual;
рrocedure Hide; virtual;
end;
tyрe
TBee = object(TWinged)
constructor Init (AX, AY : Integer)
рrocedure Show; virtual;
рrocedure Hide; virtual;
end;
Прежде всего обратите внимание, что метод MoveTo, показанный
для типа TBee, теперь удален из его определения. Теперь типу TBee
больше нет нужды переопределять метод MoveTo типа TWinged с по-
мощью немодифицируемой копии, компилируемой в его собственной об-
ласти действия. Вместо этого, MoveTo теперь может наследоваться
от TWinged со всеми вложенными в MoveTo вызовами, которые, одна-
ко, будут вызывать методы из TBee, а не из TWinged, как это про-
исходило в полностью статической иерархии объектов.
Отметьте также новое зарезервированное слово constructor
(конструктор), заменившее зарезервированное слово рrocedure для
TWinged.Init и TBee.Init. Конструктор является специальным типом
процедуры, которая выполняет некоторую установочную работу для
механизма виртуальных методов. Более того, конструктор должен вы-
зываться перед вызовом любого виртуального метода. Вызов вирту-
ального метода без предварительного вызова конструктора может
привести к блокированию системы, а у компилятора нет способа про-
верить порядок вызова методов.
Каждый тип объекта, имеющий виртуальные методы, обязан иметь
конструктор.
Предупреждение: Конструктор должен вызываться перед вызовом
любого другого виртуального метода. Вызов виртуального метода без
предыдущего обращения к конструктору может вызвать блокировку
системы, и компилятор не сможет проверить порядок, в котором вы-
зываются методы.
Примечание: Для конструкторов объекта мы предлагает
использовать идентификатор Init.
Каждый отдельный экземпляр объекта должен инициализироваться
отдельным вызовом конструктора. Недостаточно инициализировать
один экземпляр объекта и затем присваивать этот экземпляр другим.
Другие экземпляры, даже если они могут содержать правильные дан-
ные, не будут инициализированы оператором присваивания и заблоки-
руют систему при любых вызовах их виртуальных методов. Например:
var
FBee, GBee: Bee; { создать два экземпляра Bee }
begin
FBee.Init(5, 9) { вызов конструктора для FBee }
GBee := FBee; { Gbee недопустим! }
end;
Что же именно создает конструктор? Каждый тип объекта содер-
жит нечто, называемое таблицей виртуального метода (ТВМ) в сег-
менте данных. ТВМ содержит размер типа объекта и для каждого вир-
туального метода указатель на код, выполняющий данный метод.
Конструктор устанавливает связь между вызывающей его реализацией
объекта и ТВМ типа объекта.
Важно помнить, что имеется только одна ТВМ для каждого типа
объекта. Отдельные экземпляры типа объекта (т.е. переменные этого
типа) содержат только соединение с ТВМ, но не саму ТВМ. Конструк-
тор устанавливает значение этого соединения в ТВМ. Именно благо-
даря этому вы нигде не можете запустить выполнение перед вызовом
конструктора.