Мегаобучалка Главная | О нас | Обратная связь


Инициализация и разрушение объектов



2019-07-04 483 Обсуждений (0)
Инициализация и разрушение объектов 0.00 из 5.00 0 оценок




Для объявления конструктора используется зарезервированное слово constructor, после которого идет имя конструктора и параметры, если необходимо. Конструктор возвращает указатель на экземпляр класса. У конструктора Tobject имя Create, поэтому у всех потомков этого класса есть конструктор Create, хотя, у некоторых классов есть и другие конструкторы с другими именами, например у обработчиков исключений. В теле конструктора Вы можете вызвать конструктор предка для инициализации закрытой части предка значениями по умолчанию, например:

unit MyUnit;

interface

Type

TmyClass = class(TComponent)

public

constructor Create(AOwner: TComponent); override; // перегружаем конструктор предка

end;

implementation

Constructor TmyClass.Create(AOwner: TComponent);

Begin

Inherited Create(Aowner); // Вызов конструктора предка

… // Дальнейшая инициализация объекта

End;

Если имя конструктора предка совпадает с именем потомка, то можно сократить запись при вызове конструктора предка в конструкторе потомка:

Constructor TmyClass.Create(AOwner: TComponent);

Begin

Inherited (Aowner); // Вызов конструктора предка

… // Дальнейшая инициализация объекта

End;

Для уничтожение объекта служит деструктор. Деструктор объявляется с помощью зарезервированного слова destructor, после которого идет имя деструктора. Деструктор ничего не возвращает и не имеет параметров. Я советую Вам вместо прямого вызова деструктора использовать метод Free. Этот метод есть у всех классов в Delphi, т.к. наследуется от Tobject. Этот метод сначала проверяет неравенство указателя на класс nil, а затем только вызывает Destroy. Это более безопасный способ уничтожить объект.unit MyUnit;

interface

Type

TmyClass = class(TComponent)

public

constructor Create(AOwner: TComponent); override; // перегружаем конструктор предка

destructor Destroy; override // Перегружаем деструктор предка

end;

implementation

destructor TmyClass.Destroy;

Begin

… // Уничтожение объекта

Inherited Destroy; // Вызов деструктора предка, для уничтожение закрытых полей предка

End;

Инкапсуляция

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

Type

TmyClass = class(TObject)

Private

AmyField: Integer; // Объявление поля целого типа

Protected

Procedure SetMyField(Val: Integer); virtual; // Объявление процедуры для записи значения свойства

Public

Property MyField: Integer read AmyField write SetMyField; // объявление свойства

End;

Здесь мы видим, что свойство MyField является целым типом. Оно доступно для чтения и записи, т.к. объявлены методы read и write. Процедура, организующая запись значения объявлена в секции protected для того, чтобы в случае необходимости, Вы могли перегрузить ее в потомке. На самом деле значение свойства хранится в поле AmyField класса. Если Вы не объявите метода write, то свойство станет доступным только по чтению, аналогично и с методом read. К слову говоря, поля для хранения значения свойства может и не быть, главное, чтобы были объявлены методы доступа к нему. Для примера, класс, реализующий интерфейс доступа к свойствам дисплея, мог бы иметь свойства ширина и высота в пикселях. Вам не обязательно хранить эти значения, т.к. можно вызвать стандартную функцию Windows и узнать ширину и высоту экрана, тем более, что в процессе работы эти значения могут меняться при переключении в другой режим. Методы записи и чтения свойства подчиняются жестким правилам. Так для чтения свойства, Вам необходимо объявить функцию без формальных параметров, возвращающую значение того же типа, что и свойство. Для записи значения, Вам необходимо объявить процедуру с одним параметром того же типа, что и свойство. Если в качестве метода для записи или чтения свойства Вы указываете имя поля для хранения значения свойства, то это аналогично прямому доступу к этому полю. При компиляции такого способа обращения к свойству, код будет оптимизирован, поэтому это не повлечет никаких дополнительных расходов ресурсов компьютера. Обычно, такой метод обращения к полю применяют для чтения. Методы доступа к полям класса могут выполнять дополнительную работу при переустановке значения поля класса. Так, при установке свойства Ttable.Active в true производится чтение данных из таблицы базы данных на жестком диске, несколько раз меняется состояние объекта, посылаются события связанным объектам, вызываются обработчики делегированных событий, которые пишет программист, и только потом переустанавливается значение свойства в true. Для присвоения свойству значения по умолчанию, используется ключевое слово default, например:

property Visible: boolean read Avisible write SetVisible default true;Это значит, что при запуске программы, компилятор установить это свойство в true. Однако я настоятельно рекомендую Вам устанавливать значение свойств по умолчанию в конструкторе класса.Вы можете иметь индексированные свойства. Вот пример реализации такого класса.

Type

TmyClass = class(TObject);

private

AmyList: Tlist; // Контейнер указателей

Protected

Function GetMyList(Index: Integer): Pointer; // Функция доступа по чтению

Procedure SetMyList(Index: Integer; Val: Pointer); // Процедура доступа по записи

Public

Property MyList[Index: Integer]: Pointer read GetMyList write SetMyList; // Объявление индексированного свойства

End;

Здесь мы видим, что свойство MyList индексированно - это элемент списка указателей. В квадратных скобках Вам нужно указать список индексов и их типов. В общем случае, индексом может быть даже строка символов. Далее идет тип свойства и методы записи и чтения. Функция чтения должна иметь список формальных параметров со всеми индексами свойства и возвращать значение того же типа, что и свойство. Процедура записи должна иметь список формальных параметров со всеми индексами свойства и параметр для передачи устанавливаемого значения того же типа что и свойство. Большое значение имеет последовательность указания индексов и обязательность передачи значения свойства в процедуре записи последним в списке формальных параметров. Если индексированное свойство является основным и обращение именно к нему производится чаще остальных, то можно объявить его как default, тогда не нужно указывать имя свойство для доступа к нему, например:

Type

TmyClass = class(TObject);

private

AmyList: Tlist; // Контейнер указателей

Protected

Function GetMyList(Index: Integer): Pointer; // Функция доступа по чтению

Procedure SetMyList(Index: Integer; Val: Pointer); // Процедура доступа по записи

Public

Property MyList[Index: Integer]: Pointer read GetMyList write SetMyList; default; // Объявление индексированного свойства по умолчанию

End;

Var

MyClass: TmyClass;

Begin

MyClass:= TmyClass.Create;

MyClass [3]:=AnyObject; // Аналогично MyClass.MyList[3] ]:=AnyObject;

End;

Значение инкапсуляции в объектно-ориентированном программировании трудно переоценить. Чего стоит хотя бы то, что в Delphi к 100% полей классов доступ организован через свойства.

Наследование

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

Type

TmyFirstClass = class(TObject)

Private

Protected

Public

Constructor Create(Val: Integer); virtual;

end;

TmySecondClass = class(TMyFirstClass)

Private

AmyField: string; // Добавили новое поле

Protected

Procedure SetMyField(Val: string); // Добавили процедуру

Public

Constructor Create(Val: Integer); override; // Перегрузили конструктор

Property MyField: string read AmyField write SetMyField; // Добавили свойство

End;

Есть несколько правил области видимости объекта, которые помогут Вам разобраться со способами доступа к объектам и наследования объектов:

Поля, свойства и методы секции public не имеют ограничений на видимость.

Поля, свойства и методы секции private, доступны только в методах класса и в функциях, объявленных в том же модуле, где и класс.

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

При описании потомков, Вы можете изменять область видимости методов и свойств. Можно расширять область видимости, но не сужать. Т.е. если есть свойство в секции private, вы можете сделать его public, но не наоборот. Вот пример расширения области видимости:

Type

TmyClass=class(TObject)

Private

AmyField: Integer;

protected

property MyField: Integer read AmyField;

End;

TmySunClass = class(TMyClass)

Public

Property MyField; // Только упомянули его в другой секции и он поменял область видимости.

End;

Унаследованные поля и методы потомка имеются и в предке. При совпадении имен предка и потомка говорят о перекрытии потомком полей или методов предка. По способу вызова методы класса можно разделить на статические, виртуальные, динамические, перегружаемые и абстрактные. Абстрактные методы не имеют реализации и должны быть обязательно перекрыты в потомках. Абстрактными могут быть только виртуальные и динамические методы, например :

Type

TmyClass=class(TObject)

protected

procedure MyMethod(Val: Integer); virtual; abstract;

End;

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

TmyClass=class(TObject)

protected

procedure MyMethod;

End;

TmySunClass=class(TmyClass)

protected

procedure MyMethod(Val: Integer);

End;

…procedure TmySunClass.MyMethod(Val: Integer);begin

inherited MyMethod; // Метод предка без параметров, а метод потомка уже с параметром, т.е. мы поменяли тип процедуры.

end;

По умолчанию все методы - статические, поэтому их адреса известны уже на стадии компиляции, они будут вызываться при выполнении программы самым быстрым способом. Виртуальные и динамические методы описываются с помощью специальных директив virtual или dynamic. Эти методы могут быть перекрыты в потомке одноименными методами, имеющими тот же тип.

И наконец, хочу сказать, что Delphi не поддерживает множественного наследования. Для достижения того же самого, Вам придется использовать простое наследование от одного из нужных классов, а второй класс добавить как поле, и организовать доступ к свойствам второго класса через свойства наследника. Так можно соединить несколько классов. Другой способ - это создать класс-контейнер, в котором в качестве полей будут нужные включенные классы, а интерфейс доступа к ним организовать через свойства контейнера.

Полиморфизм

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

Type

TMyClass = class(TObject)

public

procedure GetData: string; virtual; abstract;

end;

TmySun1Class = class(TMyClass)

Protected

AmyField: string;

public

procedure GetData: string; override;

end;

TmySun2Class = class(TMyClass)

Protected

AmyField: Integer;

public

procedure GetData: string; override;

end;

implementation

procedure TmySun1Class.GetData: string;

begin

Result:= AmyField;

end;

procedure TmySun2Class.GetData: string;

begin

Result:=IntToStr(AmyField);

end;

Var

MyClass: TmyClass;

Class1: TmySun1Class;

Class2: TmySun2Class;

Begin

Class1:=TmySun1Class.Create;

Class2:=TmySun2Class.Create;

MyClass:= Class1;

Label1.Caption:= MyClass.GetData;

MyClass:= Class2;

Label2.Caption:= MyClass.GetData;

end;

Если посмотреть на этот код внимательно, то можно понять, что у компилятора нет возможности определить метод какого именно класса нужно вызывать. Поэтому, для определения адреса метода используются специальные таблицы, где хранятся адреса на виртуальные и динамические методы: VMT - таблица виртуальных методов и DMT - таблица динамических методов. Когда компилятор встречает указатель на виртуальный метод, то он ищет его адрес в VMT, где хранятся все адреса виртуальных методов класса унаследованных и перекрытых, поэтому такая таблица занимает много памяти, хотя и способ вызова метода работает сравнительно быстро. Динамические методы вызываются медленнее, но занимают меньше памяти, т.к. в них хранятся адреса динамических методов только данного класса и их индексы. При вызове динамических методов проводится поиск по этой таблице, если метод не найден, то поиск продолжается в DMT предков вплоть до Tobject, где вызывается стандартный обработчик вызова динамических методов. Зачем же нам все это надо? При проектировании иерархии классов предметной области, нужно статическими делать методы, которые не меняют своего поведения в потомках, т.е. при более детальном рассмотрении явления. Динамические и виртуальные методы могут меняться при переходе от общего к частному. Вспомните класс Tfield, который является общим предком для всех классов-полей таблицы. Потомки этого класса реализуют доступ к столбикам таблицы разных типов от целого числа до BLOB массива, однако, Вы можете иметь удобный доступ к этим потомкам через указатель типа Tfield и работать с ними одинаково.



2019-07-04 483 Обсуждений (0)
Инициализация и разрушение объектов 0.00 из 5.00 0 оценок









Обсуждение в статье: Инициализация и разрушение объектов

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Почему люди поддаются рекламе?: Только не надо искать ответы в качестве или количестве рекламы...
Как вы ведете себя при стрессе?: Вы можете самостоятельно управлять стрессом! Каждый из нас имеет право и возможность уменьшить его воздействие на нас...
Как построить свою речь (словесное оформление): При подготовке публичного выступления перед оратором возникает вопрос, как лучше словесно оформить свою...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (483)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.009 сек.)