Inc ax ; возвратить true
@@1: pop bp ; восстановить bp
ret8 ; извлечь параметры и выйти
Rect@ContainsENDP
code ENDS
END
Обнаружение ошибок конструктора
Как описано в Главе 16, Турбо Паскаль позволяет вам установить функцию ошибок динамически распределяемой области памяти посредством переменной HeapError модуля System. Эта функциональность поддерживается и в Турбо Паскале, однако теперь она оказывает воздействие на способ работы конструкторов.
По умолчанию, если не хватает памяти для размещения динамического экземпляра объектного типа, то вызов конструктора, использующий расширенный синтаксис стандартной процедуры New, генерирует ошибку 203 исполняющей системы. Если вы устанавливаете функцию ошибок динамической памяти, которая возвращает 1, а не стандартный результат функции 0, то вызов конструктора через New будет возвращать nil, если конструктор не сможет завершить запрос (вместо прекращения выполнения программы).
Код, который выполняет размещение и инициализацию поля таблицы виртуальных методов динамического экземпляра, является частью последовательности точки входа в конструктор. Если управление передается в точку begin операторной части конструктора, то экземпляр уже будет размещен и инициализирован. Если размещение завершилось неудачно, и если функция ошибки динамически распределяемой области памяти возвратила 1, то конструктор пропускает выполнение операторной части и возвращает указатель nil. Таким образом, указатель, который задан в расширенной процедуре New, вызывающей конструктор, будет установлен в nil (пусто).
Примечание: Имеется новая стандартная процедура Fail.
Если только управление передается в точку begin операторной части конструктора, то это гарантирует, что экземпляр объектного типа уже успешно размещен и инициализирован. Однако, сам конструктор может попытаться разместить динамическую переменную, чтобы инициализировать поле указателя экземпляра, и эта попытка может потерпеть неудачу. Если это произойдет, то конструктор с хорошо продуманным поведением должен дать обратный ход успешному размещению и в конце концов удалить размещенный экземпляр объектного типа, чтобы конечным результатом явился указатель nil. Чтобы сделать возможным этот обратный ход, Турбо Паскаль предоставляет новую стандартную процедуру Fail, которая не имеет параметров и которая может вызываться только изнутри конструктора. Вызов Fail заставляет конструктор удалить динамический экземпляр, который был размещен при входе в конструктор, и ведет к возврату указателя nil для индикации неудачной попытки.
Если динамические экземпляры размещаются с помощью расширенного синтаксиса New, то результирующее значение nil специфицированного указателя указывает на неудачную операцию. Несомненно, нет таких переменных типа указатель, которые можно было бы проверить после создания статического экземпляра или после вызова наследованного конструктора. Вместо этого, Турбо Паскаль позволяет использовать в выражениях конструкторы как функции, возвращающие результат типа boolean. Возвращаемое значение True означает успех, а False - неудачу, благодаря вызову Fail внутри конструктора.
Следующая программа предоставляет два простых объектных типа, содержащих указатели. Эта первая версия программы не использует обнаружение ошибок конструктора.
type
LinePtr = ^Line;
Line = string [79];
BasePtr = ^Base;
Base = object
L1, L2: LinePtr;
constructor Init(S1, S2: Line);
destructor Done; virtual;
procedure Dump; virtual;
end;
DerivedPtr = ^Derived;
Derived = object(base)
L3, L4: LinePtr;
constructor Init(S1, S2, S3, S4: Line);
destructor Done; virtual;
procedure Dump; virtual;
end;
var
BP: BasePtr;
DP: DerivedPtr;
constructor Base.Init(S1, S2: Line);
begin
New (L1);
New (L2);
L1^ := S1;
L2^ := S2;
end;
destructor Base.Done;
begin
Dispose(L2);
Dispose(L1);
end;
procedure Base.Dump;
begin
Writeln('B: ', L1^, ', ', L2^, '.');
end;
constructor Derived.Init(S1, S2, S3, S4: Line);
begin
Base.Init(S1, S2);
New(L3);
New(L4);
L3^ := S3;
L4^ := S4;
end;
destructor Derived.Done;
begin
Dispose(L4);
Dispose(L3);
Base.Done;
end;
procedure Derived.Dump;
begin
Writeln('D: ',L1^,', ',L2^,', ',L3^,', ',L4^,'.');
end;
begin
New(BP, Init('Турбо', 'Паскаль');
New(DP, Init('Север', 'Восток', 'Юг', 'Запад');
BP^.Dump;
DP^.Dump;
Dispose(DP, Done);
Dispose(BP, Done);
end.
Следующий пример демонстрирует, как можно переписать предыдущий пример для реализации обнаружения ошибок. Описания типов и переменных не приводятся, т.к. они остаются без изменений.
constructor Base.Init(S1, S2: Line);
begin
New(L1);
New(L2);
if (L1 = nil) or (L2 = nil) then
begin
Base.Done;
Fail;
end;
L1^ := S1;
L2^ := S2;
end;
destructor Base.Done;
begin
if L2 <> nil then
Dispose (L2);
if L1 <> nil then
Dispose(L1);
end;
constructor Derived.Init(S1, S2, S3, S4: Line);
begin
if not Base.Init(S1, S2) then
Fail;
New(L3);
New(L4);
if (L3 = nil) or (L4 = nil) then
begin
Derived.Done;
Fail;
end;
L3^ := S3;
L4^ := S4;
end;
destructor Derived.Done;
begin
if L4 <> nil then
Dispose (L4);
if L3 <> nil then
Dispose(L3);
Base.Done;
end;
{$F+}
function HeapFunc(Size: word): integer;
begin
HeapFunc := 1;
end;
{$F-}
begin
HeapError := @HeapFunc; { установить обработчик ошибок
динамически распределяемой области }
New (BP, Init ('Турбо', 'Паскаль');
New (DP, Init ('Север', 'Восток', 'Юг', 'Запад');
if (BP = nil) or (DP = nil) then
Writeln('Ошибка выделения памяти')
else
begin
BP^.Dump;
DP^.Dump;
end;
if DP <> nil then
Dispose(DP, Done);
if BP <> nil then
Dispose(BP, Done);
end.
Обратите внимание, как используются соответствующие деструкторы конструкторов Base.Init и Derived.Init для отмены любого успешного размещения перед тем, как Fail вызывается, чтобы окончательно привести к неудачному выполнению операции. Заметьте также, что в Derived.Init вызов конструктора Base.Init записан внутри выражения, благодаря чему можно проверять успешное завершение в наследуемом конструкторе.