Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ВВЕДЕНИЕ В ОБЪЕКТНО Ориентированное программиро...docx
Скачиваний:
19
Добавлен:
29.08.2019
Размер:
1.01 Mб
Скачать

Конструкторы объектов

Рассмотрим, когда и каким образом объекты создаются, то есть когда под них распределяется память и происходят начальное присваивание. Когда мы имеем дело с обычными переменными возможны два варианта: статическая и динамическая переменные. Если переменная статическая, то память под нее распределяется автоматически перед началом выполнения соответствующего блока - при запуске программы для глобальных переменных и при вызове процедуры для локальных. Если переменная динамическая, то память под нее должна быть распределена вручную вызовом оператора new. Переменные типа класс в Object Pascal описываются как статические:

Var AMyObject:TMyObject;

но именно в этом языке они могут быть только динамическими. Это означает, что переменная AMyObject является указателем, содержащим адрес объекта и должна быть распределена вручную. Хотя обращение к полям и методам производится без использования символа (^), как было бы правильно для указателей на структуру. В других объектно-ориентированных языках, например С++, это не так. В них могут быть как статические так и динамические объекты, хотя используются чаще динамические. Статические объекты создаются автоматически, динамические - в ручную. Создание объекта осуществляется путем вызова специального метода, который инициализирует объект - конструктора, Созданный экземпляр уничтожается другим методом - деструктором.

Конструктор - это специальный метод, который создает и инициализирует требуемый объект. Описание конструктора подобно описанию процедуры, но начинается со слова constructor. Например:

Constructor Create;

Constructor Create(AOwner:TComponent);

Хотя в описании отсутствует тип возвращаемой переменной, при вызове конструктора через имя класса, он возвращает ссылку на объект, который был создан.

Класс может иметь более чем один конструктор, но может иметь только один. Обычно имя конструктора Create.

Для создания объекта конструктор вызывается через указание имени класса. Именно класса, а не объекта (т.е. типа а не переменной) так как объект еще не создан. Есть такие методы (в том числе и конструктор), которые успешно работают до или даже без создания объекта. Такие методы называют методами класса. Они будут рассмотрены позднее.

Пример создания объекта:

Var MyObject:TMyObject;

MyObject:=TMyClass.Create;

При этом происходят следующие действия: распределяется динамическая память под поля объекта, значения всех скалярных полей устанавливаются в 0, всем указателям и полям типа класс присваивается nil, всем полям типа строка присваивается значение «пустая строка». Далее выполняются действия, описанные в конструкторе. Обычно это инициализация полей объекта. При этом имеет смысл инициализировать только ненулевые и непустые поля, так как остальные проинициализируются автоматически. По завершению конструктор возвращает указатель на только что распределенный и проинициализированный объект. Тип возвращаемого значения соответствует типу класса, для которого был вызван конструктор.

В случае, если конструктор вызывается через имя переменной - ссылки на объект, в отличие от вызова через имя класса, не происходит создание нового объекта и, соответственно, конструктор не возвращает значение. Выполняются только те действия, которые описаны в теле конструктора.

Как уже было сказано, конструкторов может быть несколько. При этом должно использоваться служебное слово overload, которое делает метод перегружаемым, перегрузка подробно будет рассматриваться далее.

Существуют три варианта конструкторов: конструктор по умолчанию, конструктор инициализации и конструктор копирования:

- конструктор по умолчанию обычно не имеет параметров и присваивает полям объекта значения по

умолчанию.

- конструктор инициализации получает в качестве параметров значения, которые присваиваются полям объекта.

- конструктор копирования получает в качестве параметра указатель на объект того же типа и присваивает своим полям те же значения, что и полученного объекта. То есть состояние объекта как бы копируется. При этом возникает одна проблема. Что делать с указателями, Возможны два варианта: первый - присвоить указателям создаваемого объекта значения указателей копируемого, при этом два объекта будут работать с одной областью памяти; второй - создать копию всех динамических структур и переписать все значения. Нужный вариант выбирается программистом в зависимости от преследуемых им целей. Например, если назначение объектов - обработка одного большого изображения, хранящегося в памяти, то копирование его для каждого из объектов не только трудоемко, но и логически неверно. Пример использования конструкторов:

Type

TMyObject = class;

Property A:integer read GetA write SetA;

Property B:integer read GetB write SetB;

Property C:real read GetC write SetC;

constructor Create; overload; {по умолчанию}

constructor Create (pA,pB,pC: integer); overload; {инициализация}

constructor Create(CopyMyObject:TMyObject); overload; {копирования}

end;

constructor TMyObject.Create;

begin

A:=1;

B:=1;

{C остается равным 0}

end;

constructor TMyObject.Create(pA,pB,pC:integer);

begin

A:=pA;

B:=pB;

C:=pC;

End;

constructor TMyObject.Create(CopyMyObject:TMyObject);

begin

A:=CopyMyObject.A;

B:=CopyMyO bject.B;

C:=CopyMyObject.C;

End;

. . .

Var

A1MyObject:TMyObject;

A2MyObject:TMyObject;

A3MyObject:TMyObject;

Begin

A1MyObject:=TMyObject.Create; {конструктор по умолчанию}

A2MyObject:=TMyObject.Create(10,20,0.5); {конструктор инициализации}

. . .

A1MyObject.B:=1000;

A3MyObject:=TMyObject.Create(A1MyObject); {конструктор копирования}

. . .

End;

При создании объекта A1MyObject его поля получат значения (1,1,0), объекта A2MyObject – (10,20,0.5), A3MyObject – (1,1000,0)

Конструктор копирования С++

В языке Object Pascal не существует статических переменных типа класс. Имеются только указатели, поэтому такие операции, как присвоение одного объекта другому, вызов подпрограммы с объектом в качестве параметра - это работа с указателями. В других ООЯ (например С+ +) существуют статические переменные-объекты. В этом случае конструктор копирования приобретает особое значение. Он может быть вызван явно как метод, подобно Object Pascal, и, кроме того, он может быть вызван неявно - при инициализации объекта данного класса другим объектом и при передаче объекта в качестве параметра процедуры по значению, Рассмотрим последний случай - передача параметров по значению. В этом случае создается копия объекта, с которой и производятся определенные в процедуре действия, изменения состояния объекта при вызове его методов будут происходить с этой копией и никак не отразятся на оригинале.

В случае, когда программист не описал конструктор копирования, вызывается специальный конструктор, действующий по умолчанию, который копирует объект поэлементно. Однако, когда объект содержит динамические переменные или указатели на другие объекты, происходит копирование указателей без копирования областей памяти, на которые они указывают, что делает поэлементное копирование опасным. Предлагается эмпирическое правило: разрешать неявное копирование только для примитивных объектов, не содержащих указателей, и всегда описывать конструктор копирования для более сложных объектов.

Возможен случай, когда в описании класса отсутствует конструктор, в этом случае автоматически конструктор по умолчанию с именем Create, который не выполняется при создании объекта никаких дополнительных действий, за исключением вызова конструктора предка, что будет рассмотрено далее. То есть все переменные остаются 0 или nil.

Type

TMyObject=class;

A:integer;

B:integer;

C:real;

. . .

End;

. . .

Var

AMyObject:TMyObject;

Begin

AMyObject:=TMyObject.Create; {конструктор по умолчанию}

. . .

End;

Приведем пример. Дополним описание класса стек из прошлого примера конструкторами по умолчанию, так как использование других типов конструкторов в данном случае неестественно.

1. Стек в массиве

TSObStack = class (TObject)

Stack:array[1..1000] of TElem;

Top: integer;

Constructor Create; {по умолчанию}

procedure InStack(Elem: TElem) ;

function OutStack(var Elem: TElem) :boolean;

end;

Constructor TSObStack .Create;

Begin

Top:=0; {Можно было и не делать} { конструктор по умолчанию не нужен}

End;

Описание объекта по-прежнему не является полным, отсутствуют элемент, отвечающий за уничтожение объекта объекта. Он будет рассмотрен позднее.

2. Динамический стек

TDObStack = class (TObject)

Top:TERef

Constructor Create; {умолчанию}

. . .

procedure InStack (Elem: TElem) ;

function OutStack(var Elem: TElem) :boolean;

. . .

end;

Constructor TDObStack .Create;

Begin

Top:=nil; {можно было и не делать} {конструктор по умолчанию не нужен}

End;

Использование:

var

DStack:TDObStack;

SStack:TSObStack;

begin

DStack: =TDObStack.Create ;

SStack: =TSObStack.Create;

end.