Таблица виртуальных методов
Каждый объектный тип, содержащий или наследующий виртуальные методы, конструкторы или деструкторы, имеет связанную с ним таб- лицу виртуальных методов, в которой запоминается инициализируемая часть сегмента данных программы. Для каждого объектного типа (но не для каждого экземпляра) имеется только одна таблица виртуаль- ных методов, однако два различных объектных типа никогда не раз- деляют одну таблицу виртуальных методов, независимо от того, нас- колько эти типы идентичны. Таблицы виртуальных методов создаются автоматически компилятором, и программа никогда не манипулирует ими непосредственно. Аналогично, указатели на таблицы виртуальных методов автоматически запоминаются в реализациях объектных типов с помощью конструкторов программа никогда не работает с этими указателями непосредственно.
Первое слово таблицы виртуальных методов содержит размер экземпляров соответствующего объектного типа. Эта информация ис- пользуется конструкторами и деструкторами для определения того, сколько байт выделяется или освобождается при использовании рас- ширенного синтаксиса стандартных процедур New и Dispose.
Второе слово таблицы виртуальных методов содержит отрица- тельный размер экземпляров соответствующего объектного типа эта информация используется ратификационным (т.е. подтверждающим действительность) механизмом вызова виртуального метода для выяв- ления инициализируемых объектов (экземпляров, для которых должен выполняться конструктор) и для проверки согласованности таблицы виртуальных методов. Когда разрешена ратификация виртуального вы- зова (с помощью директивы {$R+} компилятора, которая расширена и включает в себя проверку виртуальных методов), компилятор генери- рует вызов программы ратификации таблицы виртуальных методов пе- ред каждым вызовом виртуального метода. Программа ратификации таблицы виртуальных методов проверяет, что первое слово таблицы виртуальных методов не равно нулю и что сумма первого и второго слов равна нулю. Если любая из проверок неудачна, то генерируется ошибка 210 исполняющей системы Borland Pascal.
Разрешение проверок границ диапазонов и проверок вызовов виртуальных методов замедляет выполнение программы и делает ее несколько больше, поэтому используйте {$R+} только во время от- ладки и переключите эту директиву в состояние {$R-} в окончатель- ной версии программы.
Наконец, начиная со смещения 4 таблицы виртуальных методов следует список 32-разрядных указателей методов, один указатель на каждый виртуальный метод в порядке их описаний. Каждая позиция содержит адрес точки входа соответствующего виртуального метода.
На Рис. 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 ¦ ¦ ¦ ¦ ¦ L---------------- +----------------+ ¦ ¦ ¦ @TCircle.Fill ¦ ¦ ¦ L-----------------
Рис. 21.9 Схемы таблиц виртуальных методов для TPoint и TCircle.
Обратите внимание на то, как TCircle наследует методы Done и MoveTo типа TPoint и как он переопределяет Show и Hide.
Как уже упоминалось, конструкторы объектных типов содержат специальный код, который запоминает смещение таблицы виртуальных методов объектного типа в инициализируемых экземплярах. Например, если имеется экземпляр P типа TPoint и экземпляр C типа TCircle, то вызов P.Init будет автоматически записывать смещение таблицы виртуальных методов типа TPoint в поле таблицы виртуальных мето- дов экземпляра P, а вызов C.Init точно так же запишет смещение таблицы виртуальных методов типа TCircle в поле таблицы виртуаль- ных методов экземпляра C. Эта автоматическая инициализация явля- ется частью кода входа конструктора, поэтому если управление пе- редается в начало операторной секции, то поле Self таблицы вирту- альных методов также будет установлено. Таким образом, при воз- никновении необходимости, конструктор может выполнить вызов вир- туального метода.