Type имя типа=Object

описание полей описание методов 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.