Виртуальные методы

Вверх  Предыдущий  Следующий

Чтобы исправить ситуацию в предыдущем разделе, создаются virtual (виртуальные) методы. Это делается достаточно просто, путем добавления модификатора virtual к объявлению метода. Потомки объекта могут переопределить метод с новой реализацией путем повторного объявления метода (с тем же списком параметров) с помощью ключевого слова virtual.

Возвращаясь к предыдущему примеру, рассмотрим следующую альтернативу объявлению:

Type

TParent = Object

  ...

  procedure Doit;virtual;

  ...

  end;

PParent = ^TParent;

TChild = Object(TParent)

  ...

  procedure Doit;virtual;

  ...

  end;

PChild = ^TChild;

Как видно, оба объекта родитель и ребенок имеют метод, называемый DoIt. Рассмотрим следующие объявления и вызовы:

Var

ParentA,ParentB : PParent;

Child : PChild;

 

begin

  ParentA := New(PParent,Init);

  ParentB := New(PChild,Init);

  Child := New(PChild,Init);

  ParentA^.Doit;

  ParentB^.Doit;

  Child^.Doit;

Теперь, будут вызваны различные методы, в зависимости от фактического типа объекта во время выполнения программы. Для ParentA, ничего не изменится, так как она создается как экземпляр класса TParent. Для Child, ситуация также не меняется: она вновь создается как экземпляр TChild.

Однако для ParentB, ситуация меняется: Даже если она была объявлена как TParent, она будет создана как экземпляр TChild. Теперь, когда программа работает, прежде чем вызывать DoIt, программа проверяет, какой фактический тип имеет ParentB, и только потом решает, какой метод должен быть вызван. Видя, что ParentB имеет тип TChild, будет вызван TChild.Doit. Код времени выполнения для проверки фактического типа экземпляра объекта вставляется компилятором во время компиляции.

Говорят что TChild.Doit override (переопределяет) TParent.Doit. Можно получить доступ к TParent.Doit изнутри TChild.Doit, с помощью ключевого слова inherited:

Procedure TChild.Doit;

begin

inherited Doit;

...

end;

В приведенном выше примере, когда вызывается метод TChild.Doit, первое, что он делает, это вызов TParent.Doit. Ключевое слово inherited не может быть использовано в статических методах, только в виртуальных.

Для того, чтобы сделать это, компилятор хранит - для типа объекта - таблицы с виртуальными методами: VMT (Virtual Method Table). Это просто таблица с указателями на каждый из виртуальных методов: каждый виртуальный метод имеет свои фиксированные места в этой таблице (индекс). Компилятор использует эту таблицу, чтобы найти фактический метод, который должен быть использован. Когда потомок объекта переопределяет метод, родительский метод будет переписан в VMT. Более подробную информацию о VMT можно найти в Справочник программиста Free Pascal.

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