Каждый объектный тип, содержащий или наследующий виртуальные
методы, конструкторы или деструкторы, имеет связанную с ним таб-
лицу виртуальных методов, в которой запоминается инициализируемая
часть сегмента данных программы. Для каждого объектного типа (но
не для каждого экземпляра) имеется только одна таблица виртуаль-
ных методов, однако два различных объектных типа никогда не раз-
деляют одну таблицу виртуальных методов, независимо от того, нас-
колько эти типы идентичны. Таблицы виртуальных методов создаются
автоматически компилятором, и программа никогда не манипулирует
ими непосредственно. Аналогично, указатели на таблицы виртуальных
методов автоматически запоминаются в реализациях объектных типов
с помощью конструкторов программа никогда не работает с этими
указателями непосредственно.
Первое слово таблицы виртуальных методов содержит размер
экземпляров соответствующего объектного типа. Эта информация ис-
пользуется конструкторами и деструкторами для определения того,
сколько байт выделяется или освобождается при использовании рас-
ширенного синтаксиса стандартных процедур New и Dispose.
Второе слово таблицы виртуальных методов содержит отрица-
B.Pascal 7 & Objects/LR - 379 -
тельный размер экземпляров соответствующего объектного типа эта
информация используется ратификационным (т.е. подтверждающим
действительность) механизмом вызова виртуального метода для выяв-
ления инициализируемых объектов (экземпляров, для которых должен
выполняться конструктор) и для проверки согласованности таблицы
виртуальных методов. Когда разрешена ратификация виртуального вы-
зова (с помощью директивы {$R+} компилятора, которая расширена и
включает в себя проверку виртуальных методов), компилятор генери-
рует вызов программы ратификации таблицы виртуальных методов пе-
ред каждым вызовом виртуального метода. Программа ратификации
таблицы виртуальных методов проверяет, что первое слово таблицы
виртуальных методов не равно нулю и что сумма первого и второго
слов равна нулю. Если любая из проверок неудачна, то генерируется
ошибка 210 исполняющей системы Borland Pascal.
Разрешение проверок границ диапазонов и проверок вызовов
виртуальных методов замедляет выполнение программы и делает ее
несколько больше, поэтому используйте {$R+} только во время от-
ладки и переключите эту директиву в состояние {$R-} в окончатель-
ной версии программы.
Наконец, начиная со смещения 4 таблицы виртуальных методов
следует список 32-разрядных указателей методов, один указатель на
каждый виртуальный метод в порядке их описаний. Каждая позиция
содержит адрес точки входа соответствующего виртуального метода.
B.Pascal 7 & Objects/LR - 380 -
На Рис. 21.9 показано размещение таблиц виртуальных методов
типов Point и Circle (тип Location не имеет таблицы виртуальных
методов, т.к. не содержит в себе виртуальных методов, конструкто-
ров и деструкторов): каждый маленький прямоугольник соответствует
одному слову памяти, а каждый большой прямоугольник - двум словам
памяти.
Point VMT Circle VMT
┌───────────────┐ ┌────────────────┐
│ 8 │ │ 8 │
├───────────────┤ ├────────────────┤
│ -8 │ │ -8 │
├───────────────┤ ├────────────────┤
│ 0 │ │ 0 │
├───────────────┤ ├────────────────┤
│ 0 │ │ 0 │
├───────────────┤ ├────────────────┤
│ │ │ │
│ @TPoint.Done │ │ @TPoint.Done │
│ │ │ │
├───────────────┤ ├────────────────┤
│ │ │ │
│ @TPoint.Show │ │ @TCircle.Show │
│ │ │ │
├───────────────┤ ├────────────────┤
│ │ │ │
│ @TPoint.Hide │ │ @TCircle.Hide │
│ │ │ │
├───────────────┤ ├────────────────┤
│ │ │ │
│ @TPoint.MoveTo│ │ @TPoint.MoveTo │
│ │ │ │
└───────────────┘ ├────────────────┤
│ │
│ @TCircle.Fill │
│ │
└────────────────┘
Рис. 21.9 Схемы таблиц виртуальных методов для TPoint и
TCircle.
Обратите внимание на то, как TCircle наследует методы Done и
MoveTo типа TPoint и как он переопределяет Show и Hide.
Как уже упоминалось, конструкторы объектных типов содержат
специальный код, который запоминает смещение таблицы виртуальных
методов объектного типа в инициализируемых экземплярах. Например,
если имеется экземпляр P типа TPoint и экземпляр C типа TCircle,
то вызов P.Init будет автоматически записывать смещение таблицы
виртуальных методов типа TPoint в поле таблицы виртуальных мето-
дов экземпляра P, а вызов C.Init точно так же запишет смещение
таблицы виртуальных методов типа TCircle в поле таблицы виртуаль-
B.Pascal 7 & Objects/LR - 381 -
ных методов экземпляра C. Эта автоматическая инициализация явля-
ется частью кода входа конструктора, поэтому если управление пе-
редается в начало операторной секции, то поле Self таблицы вирту-
альных методов также будет установлено. Таким образом, при воз-
никновении необходимости, конструктор может выполнить вызов вир-
туального метода.