описание полей описание методов End;
Поля объектов описываются так же, как поля записей, а описание метода - это заголовок процедуры или функции. Сами методы располагаются в программе после описания объектного типа. В заголовке процедуры или функции указывается ее составное имя: имя типа.имя метода. Все поля объекта непосредственно доступны в каждом из его методов, их не нужно передавать через список параметров, это свойство объектов называется инкапсуляция. В программе можно описать любое количество переменных объектного типа. Объектом принято называть объектный тип, а переменную такого типа - экземпляром объекта. Структура объекта похожа на структуру записи, и для обращения к полям и методам объектов также используется либо составное имя
имя экземпляра объекта .имя поля/метода
либо оператор присоединенияWith имя экземпляра объекта, внутри которого можно записывать простые имена полей и методов. Экземпляры одного и того же объекта можно присваивать друг другу. Приведем тривиальный пример объекта.
Type ObjT1=Object {этот объект может вычислять xn}
x : Real;
n : Word;
Function Power:Real;
{методу не нужно передавать x и n, он “знает” их согласно свойству инкапсуляции}
End;
Function ObjT1.Power:Real;{здесь необходимо указать составное имя метода}
Var
i : Word;
p : Real;
Begin
p:=1;
For i:=1 To n Do p:=p*x;
Power:=p;
End;
Var O1_1,O1_2 : ObjT1; {два экземпляра объекта ObjT1}
Begin
With O1_1 Do Begin x:=2; n:=4; End;
{использовали оператор присоединения}
O1_2 .x:=3; O1_2 .n:=3; {использовали полные имена полей}
WriteLn(O1_2.Power-O1_1.Power:4:1);
End.
Программа выведет: 11.0
Другим важным свойством объектов является свойство наследования. Объект может быть объявлен потомком ранее описанного объекта и унаследовать от объекта-родителя все его поля и методы. Объект-потомок может также иметь собственные поля и методы, которых не было у объекта-родителя. Объект-потомок описывается так:
Object(имя родителя)
описание новых полей
описание новых методов
End;
Экземпляру объекта-родителя можно присваивать экземпляр объекта-потомка, но не наоборот. Запишем пример наследования:
Type
ObjT1=...;
ObjT2= Object(ObjT1) {этот объект может вычислять xn и xy}
y:Real;
Function RealPower:Real;
End;
ObjT3=Object(ObjT2) {этот объект может вычислять xn , xy и 10y}
Function Power10:Real;
End;
Function ObjT1.Power...;
Function ObjT2.RealPower:Real; Begin RealPower:=Exp(y*Ln(x)); End;
Function ObjT3.Power10:Real; Begin Power10:=Exp(y*Ln(10)); End;
Var
O1 : Objt1;
O2 : ObjT2;
O3 : ObjT3;
Begin
With O3 Do Begin
x:=2;
y:=1/3;
n:=5;
End;
O2:=O3; {родителю присвоили потомка}
O1:=O3;
WriteLn(O3.Power10-O2.RealPower+O1.Power:7:4);
End.
Программа выведет: 32.8945 = 101/3-21/3+25 .
Объект-потомок может заменять методы объекта-родителя на собственные методы с теми же именами. Это свойство объектов называется полиморфизмом. Приведем простой пример полиморфизма.
Type
ObjT1=...;
ObjT2=...;
ObjT3=...;
ObjT4=Object(ObjT3) {этот объект вместо xn вычисляет xt}
Function Power(t:Integer):Real;
End;
Function ObjT1.Power...
Function ObjT2.RealPower...
Function ObjT3.Power10...
Function ObjT4.Power(t:Integer):Real;
Var
i : Word;
p : Real;
Begin
p:=1;
For i:=1 To ABS(t) Do p:=p*x;
If t<0 Then Power:=1/p Else Power:=p;
End;
Var O4 : ObjT4;
Begin
O4.x:=2;
With O4 Do WriteLn(Power(2)-Power(-2):5:2);
End.
Программа выведет: 3.75 = 22-2-2. Однако замененные в объекте-потомке родительские методы остаются доступными; чтобы обратиться к ним внутри любого метода потомка, используется ключевое слово Inherited (унаследованный). Перепишем метод Power объекта ObjT4, пользуясь этой возможностью:
Function ObjT4.Power(t:Integer):Real;
Begin
If t>=0 Then Begin
n:=t;
Power:=Inherited Power;
End
Else Begin
n:=-t;
Power:=1/Inherited Power;
End;
End;
Некоторые методы объекта могут быть виртуальными. При описании таких методов в объекте после заголовка процедуры или функции записывается конструкция Virtual; Объект, содержащий хотя бы один виртуальный метод, должен иметь и специальный метод - конструктор. Конструктор полностью тождествен процедуре, но слово Procedure в нем заменяется на слово Constructor. Виртуальные методы присоединяются к объекту не на этапе компиляции, а только при вызове конструктора (при этом содержимое конструктора не имеет никакого значения, он может быть и пустым). Конструкторы не могут быть виртуальными. Невиртуальные методы называются статическими. Объекты-потомки могут заменять родительские виртуальные методы только виртуальными, а родительские статические методы - только статическими. Если объект-потомок заменяет родительский виртуальный метод своим, то у нового метода должен быть точно такой же список параметров, как и у родительского. На статические методы это правило не распространяется. Приведем очень простой пример, иллюстрирующий различие между статическими и виртуальными методами.
Type
TA = Object
Procedure Out;
Function Message:String;
End;
TB = Object(TA)
Function Message:String;
End;
Procedure TA.Out; Begin WriteLn(Message); End;
Function TA.Message:String; Begin Message:='TA'; End;
Function TB.Message:String; Begin Message:='TB'; End;
Var
A :TA;
B :TB;
Begin
A.Out;
B.Out;
WriteLn(B.Message);
End.
Программа выведет: TA TA TB. Метод Out родительского объекта TA собирается полностью; в частности, к нему подключается метод Message объекта TA. Замена метода Messageв объекте TB уже никак не может повлиять на унаследованный этим объектом статический метод Out. Теперь сделаем метод Message виртуальным.
Type
TA= Object
Procedure Out;
Function Message:String; Virtual;
Constructor Init;
{конструктор должен быть, так как есть виртуальный метод}
End;
TB= Object(TA)
Function Message:String; Virtual;
Constructor Init;
End;
Procedure TA.Out; Begin WriteLn(Message); End;
Function TA.Message:String; Begin Message:='TA'; End;
Function TB.Message:String; Begin Message:='TB'; End;
Constructor TA.Init; Begin End; {оба конструктора пустые}
Constructor TB.Init; Begin End;
Var
A : TA;
B : TB;
Begin
A.Init;
A.Out;
B.Init;
B.Out;
End.
Программа выведет: TA TB. МетодMessage является виртуальным, это значит, что компилятор не подключает этот метод к процедуре Outдо выполнения конструктора. Какая именно функция будет использоваться в качестве метода Messageпосле компиляции, еще не известно. Выполнение оператора A.Init приводит к подстановке вместо неопределенного виртуального метода Message конкретной функции TA.Message. Если виртуальных методов в объекте несколько, эта операция выполняется для каждого из них при вызове конструктора. Аналогично при вызове B.Init виртуальный метод Message в экземпляре объекта B будет заменен на функцию TB.Message всюду, где этот метод используется, в том числе и внутри метода Out. Использование в объектах виртуальных методов предполагает, что потомки данного объекта будут изменять эти методы. Если изменение метода не ожидается, то он объявляется статическим.
Экземпляры объектов можно размещать в динамической памяти. Для этого используется либо процедура New:
New(указатель на объект[,конструктор]);
либо функция New:
указатель на объект:=New(тип объекта [,конструктор]);
Конструктор обязательно указывается для объектов, имеющих виртуальные методы, и задается своим простым именем. В динамически размещаемых объектах можно использовать специальный метод-деструктор. Деструктор - это процедура, в которой ключевое слово Procedure заменяется словом Destructor. Динамически размещенный объект уничтожается процедурой
Dispose(<указатель на объект>[,<деструктор>]);
В деструкторе можно предусмотреть все необходимые действия по очистке памяти. Приведем пример объекта, размещаемого в динамической памяти:
Type
MType=Array[1..100] Of Word;
MPtrType=^MType;
ObjType = Object
n:Byte;
p:MPtrType;
Procedure Fill; Virtual;
Procedure Out; Virtual;
Constructor Make;
Destructor Crush;
End;
PObjType = ^ObjType;
Procedure ObjType.Fill;
Var i:Byte;
Begin
For i:=1 To n Do p^[i]:=Random(100);
End;
Procedure ObjType.Out;
Var i:Byte;
Begin
For i:=1 To n Do Write(p^[i]:4);
WriteLn;
End;
Constructor ObjType.Make; Begin New(p); End;
Destructor ObjType.Crush; Begin Dispose(p); End;
Var X,Y : PObjType;
Begin
X:=New(PObjType,Make); {обращаемся к New как к функции, конструктор вызывается автоматически}
With X^ DO Begin
n:=40;
Fill;
Out;
End;
New(Y); {обращаемся к New как к процедуре}
With Y^ Do Begin
Make; {конструктор вызываем “вручную”}
n:=20;
Fill;
Out;
Crush; {деструктор вызываем “вручную”}
End;
Dispose(X,Crush); {деструктор вызывается автоматически}
Dispose(Y);
End.