
Лабораторная работа №4
Обработка ошибок при работе с объектами
При использовании динамических объектов вероятна ситуация, когда для размещения экземпляра объекта не хватает памяти. Чтобы корректно обработать эту ситуацию, можно использовать процедуру Fail, которая отменяет все проделанные до данного момента определения, и освобождает экземпляр, размещенный в памяти...
При этом ссылка на экземпляр объекта становится равной nil.
{ Динамический объект }
Type
arrType = Array[1 .. 1000] of Integer;
PTArray = ^TArray;
TArray = Object
A: ^arrType;
Constructor Init;
Destructor Done; Virtual;
...
End;
Constructor TArray.Init;
begin
new(A);
{ Если будет обнаружена нехватка памяти }
if A = nil then begin
TArray.Done; Fail { Производим откат }
end;
end;
var arr: PTArray;
begin
new(arr, init); { Если в конструкторе обнаружилась нехватка памяти }
If arr = nil then halt(1); { выйти из программы }
end.
Также нехватка памяти может произойти при использовании статических объектов с полями, размещаемыми в динамической памяти. В этом случае при вызове Fail передать nil в ссылку невозможно (объект-то статический), и выходом является использование самого имени конструктора как логической функции:
{ Статический объект }
Type
arrType = Array[1 .. 1000] of Integer;
TArray = Object
A: ^arrType;
Constructor Init;
Destructor Done; Virtual;
...
End;
Constructor TArray.Init;
begin
new(A);
{ Если будет обнаружена нехватка памяти }
if A = nil then begin
TArray.Done; Fail { Производим откат }
end;
end;
Var arr: PTArray;
begin
If not arr.Init { Если в конструкторе обнаружилась нехватка памяти }
Then halt(2); { выйти из программы }
end.
Лабораторная работа №5 Функция TypeOf
Часто возникает ситуация, когда необходимо проверить фактический тип экземпляра объекта. Для этого используется функция:
TypeOf(ИмяЭкземпляра_или_ИмяТипа): Pointer;
, которая возвращает указатель на таблицу VMT для этого экземпляра или типа объекта... Параметром, передаваемым в функцию должен быть экземпляр или тип, имеющий VMT, иначе произойдет ошибка.
Проверить фактический тип параметра можно, например, так:
type
a = object
p: integer;
constructor init;
procedure x; virtual;
end;
b = object(a)
constructor init;
end;
constructor a.init;
begin end;
constructor b.init;
begin end;
procedure a.x;
begin end;
procedure check(var x: a);
begin
if typeof(x) = typeof(a) then
writeln('the object is A')
else writeln('the object is B')
end;
var
_a: A;
_b: B;
begin
_a.init; _b.init;
check(_a);
check(_b);
end.
Лабораторная работа №6 Виртуальные методы
Из правил совместимости фактических и формальных параметров типа Object следует, что в качестве фактического параметра может выступать объект любого производного от формального параметра типа. Таким образом, во время компиляции процедуры неизвестно, объект какого типа будет ей передан как фактический параметр.
В полной мере полиморфизм достигается не только механизмом наследования и перекрытия методов родителя, но и виртуализацией, позволяющей родительским методам обращаться к методам потомков.
Метод становится виртуальным, когда за его определением в типе объекта ставится зарезервированное слово Virtual.
В следующем коротком коде, реализованна виртуализация метода.
uses crt;
Type
_A = Object
per:integer;
constructor Init;
procedure P; virtual;
procedure run;
end;
_B = Object(_A)
constructor INIT;
procedure P; virtual;
end;
constructor _A.INIT;
begin per:=0 end;
procedure _A.P;
begin end;
procedure _A.RUN;
begin P end;
constructor _B.INIT;
begin per:=0 end;
Procedure _B.p;
begin per:=5; writeln(per) end;
Var
A:_A; B:_B;
Begin
Clrscr;
B.INIT;
B.run;
end.
Запустим ее, и увидим, что на экране окажется "5". Проследим выполнение программы...
При вызове процедуры B.Init (почему она называется конструктор - чуть дальше), запускается метод из объекта потомка _B. При этом этот потомок знает и умеет все, что знал и умел его предок. Значит объект _B "знает" о методе Run.
Так и есть запускаем его следующей строчкой:
B.run;
Проследим выполнение этого метода. Этот метод, в свою очередь запускает метод P. Теперь смотрим метод P:
Procedure _A.P; begin end;
Вопрос: откуда на экране появилась пятерка?
Ответ: метод P был объявлен виртуальным:
procedure P; virtual;
Если метод объявляется виртуальным, это значит, что объект-родитель сможет использовать метод объекта-потомка (!). Это очень важное правило.
Чтобы убедиться, что это так, просто закоментируйте Virtual в коде (в обоих методах), и запустите новый код; на экране, естественно, окажется "0", т.к. родитель не может обращаться к методу потомка (теперь этот метод НЕ является виртуальным, и родитель просто не знает о его существовании).
Как же метод родителя узнаёт, что надо запускать не свой метод, а перекрытый виртуальным? При трансляции объекта, содержащего виртуальные методы, создается так называемая "Таблица Виртуальных Методов - ТВМ" (английское название - VMT: Virtual Methods Table), количество элементов которой равно количеству виртуальных методов. В этой таблице будут храниться адреса точек входа в каждый метод. Такая таблица создается автоматически, с помощью специальной процедуры - конструктора.
Кроме этого, при использовании зарезервированного слова Virtual должны выполняться еще 2 условия:
Если прародитель описывает метод, как виртуальный, то он должен описываться как виртуальный и во всех потомках. Другими словами, виртуальный метод не может заменяться статическим (при попытке это сделать компилятор выдаст ошибку #149: VIRTUAL expected - "Ожидается служебное слово Virtual"). Обратное переопределение (преобразование статической функции в виртуальную) вполне допустимо.
Если переопределяется реализация виртуального метода, то его заголовок должен остаться неизменным (т.е. НЕ должны изменяться порядок расположения, количество и типы формальных параметров в одноименных виртуальных методах). При изменении заголовка компилятор выдаст ошибку #131: Header does not match previous definition - "Заголовок не соответствует предыдущему описанию"