| if ComPtr=nil { реакция на неудачу операции }
| then Halt( 2 );
| (* Применение методов Vec.Work и ComPtr^.Work *)
| Vec.Done; { освобождение поля Vec.V }
| Dispose(ComPtr, Done); { освобождение объекта ComPtr^ }
| END.
Рис. 13.8
- 292 -
Обращаем внимание на вызовы деструкторов в Vector.Init и Complex.Init перед вызовом процедуры Fail. Они нужны для отмены всех успешных размещений полей. Также важно то, что в Complex.Init вызов Vector.Init записан в выражении таким образом, что можно проверить успешность выполнения конструктора прародителя.
13.7. Функции TypeOf и SizeOf
Турбо Паскаль версии 5.5 вводит стандартную функцию
TypeOf( ИмяЭкзОбъекта_или_ИмяТипа ) : Pointer
которая возвращает указатель на таблицу виртуальных методов для конкретного экземпляра или самого типа объекта. Функция TypeOf имеет один параметр, который может быть либо идентификатором типа объекта, либо идентификатором экземпляра этого типа. Функция TypeOf применима только к объектам, имеющим таблицу VMT. Применение ее к другим типам объектов вызовет ошибку.
Функция TypeOf может быть использована для проверки фактического типа экземпляра, например:
if TypeOf( Self ) = TypeOf( ObjVar ) then ... ;
Стандартная функция Турбо Паскаля SizeOf при применении к экземпляру типа «объект», который имеет связь с таблицей VMT, возвращает размер, хранящийся в таблице VMT. Таким образом, для типов объектов, которые имеют таблицу виртуальных методов, функция SizeOf всегда возвращает фактический размер экземпляра, который может отличаться от декларированного в описании типа.
Следует сказать, что из-за сложного внутреннего представления объектов становятся опасными операции приведения типов объектов или совмещения экземпляров директивой absolute.
Программа может сама проверять корректность объектов, анализируя их размеры. Для этого программа должна компилироваться в режиме {$R+}, который распространен на виртуальные методы. При этом генерируется вызов подпрограммы проверки правильности VMT перед каждым вызовом виртуального метода. Если контрольные значения размеров в VMT указывают на сбой, происходит фатальная ошибка 210.
Включение механизма проверки вызовов виртуальных методов замедляет работу программы и несколько увеличивает ее размер. Поэтому имеет смысл включать режим {$R+} только во время отладки программы, а для рабочей версии устанавливать режим {$R-}.
- 293 -
13.8. Задание стартовых значений объектам
Можно объявлять статические объекты со стартовыми значениями полей. Для этого используется тот же синтаксис, что и для задания констант типа «запись». Для методов стартовых величин просто не существует, и они не задаются:
CONST
CObjPos : ObjPos = (Line: 1; Col: 1);
CObjString : ObgString = (Line: 1; Col:2; SubSt: 'ПЭВМ');
При таком способе инициализации полей уже нет необходимости в вызове конструктора для объектов, содержащих виртуальные методы. Инициализация обрабатывается компилятором автоматически.
Используя подобные введенным выше типизированные константы, можно инициализировать поля других объектов. Для этого достаточно ввести дополнительный метод, например Copy:
| TYPE
| Obj = OBJECT
| Поля данных;
| CONSTRUCTOR Copy(X : Obj);
| Другие конструкторы и(или) методы;
| END;
| CONSTRUCTOR Obj.Copy(X : Obj);
| BEGIN
| Self := X
| END;
| CONST
| CObj : Obj = (значения полей данных);
После этого можно вызывать этот конструктор, передавая ему в качестве параметра константу CObj типа Obj.
13.9. Модули, экспортирующие объекты
Сама реализация типа «объект» наводит на мысль об использовании модулей для определения объектов. Типы объектов можно описывать в интерфейсном разделе модуля, а тела процедур и функций, реализующих методы объектов, определяются в разделе реализации модуля. Если необходимо экспортировать переменные типа «объект», причем содержащие виртуальные методы, то для таких переменных в разделе инициализации можно разместить вызовы конструкторов. Модули могут иметь свои собственные опре-
- 294 -
деления типов объектов в разделе реализации: такие типы подчиняются тем же ограничениям, что и любые другие типы, определенные в разделе реализаций модуля. Тип объекта, определенный в интерфейсном разделе модуля, может иметь производные типы в разделе реализации модуля. В случае, когда модуль B использует модуль A, модуль В может определять производные типы от любого типа объекта, экспортируемого модулем A.