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


Создание и уничтожение объектов



2020-02-03 200 Обсуждений (0)
Создание и уничтожение объектов 0.00 из 5.00 0 оценок




Основы объектно-ориентированного программирования

Понятие класса.

Классом в Delphi называется особый тип, который может иметь в своем составе поля, методы и свойства. Такой тип также называют объектным типом.

type
tMyClass=class(tObject)
fMyFiled: integer;
function MyMethod: integer;
end;

Поля класса аналогичны полям записи. Это - данные, уникальные в программе для каждого созданного в программе экземпляра класса. Описанный выше класс tMyClass имеет одно поле - fMyFiled. В отличие от полей, методы у двух экземпляров одного класса общие. Методы - это процедуры и функции, описанные внутри класса, и предназначенные для операций над его полями. В состав класса входит указатель на специальную таблицу - таблицу виртуальных методов (VMT), в которой содержится вся информация, необходимая для вызова методов. От обычных процедур и функций методы отличаются тем, что при вызове в них передается указатель на экземпляр класса, который их вызвал. Поэтому обрабатываться будут поля именно того объекта, который вызвал метод. Внутри методов указатель на вызвавший их объект доступен через зарезервированное слово Self. Свойством класса называется совокупность поля и методов чтения/записи, обеспечивающих доступ к этому полю. При этом само поле декларируется как private (доступно только внутри данного класса), и доступ к нему возможен только посредством соответствующих методов. Подробнее свойства будут обсуждаться ниже.

Понятие объекта

Чтобы использовать новый тип в программе, нужно, как минимум, определить переменную этого типа. Переменная объектного типа называется экземпляром типа или объектом:

var aMyObject: tMyClass;

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

Создание и уничтожение объектов

В отличие от С++ и Turbo Pascal в Delphi объекты могут быть только динамическими!!!. Это означает, что в приведенном выше примере переменная aMyObject на самом деле является указателем, содержащем адрес объекта.

Объект создается конструктором и уничтожается деструктором.

aMyObject := tMyClass.Create;
//
// действия с созданным объектом
//
aMyObject.Destroy;

Следует обратить внимание на то, что для создания объекта aMyObject вызывается метод класса tMyClass.Create. Конструктор класса (и ряд других методов) успешно работает и до создания объекта. Однако большинство обычных методов (в частности все виртуальные и динамические методы). Вызывать до инициализации объекта не следует.

В Delphi конструкторов у класса может быть несколько. Общепринято называть конструктор Create, в отличие от Turbo Pascal, где конструкторы назывались Init, и С++, в котором имя конструктора совпадает с именем класса. Типичное название деструктора - Destroy.

type
tMyClass=class(tObject)
fMyFiled: integer;
Constructor Create;
Destructor Destroy;
function MyMethod: integer;
end;

Для уничтожения объекта в Delphi рекомендуется использовать не деструктор, а метод Free, который первоначально проверяет указатель, и только затем вызывает деструктор Destroy:

procedure tObject.Free;

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

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

Чтобы правильно проинициализировать в создаваемом объекте поля, относящиеся к классу - предку, нужно сразу же при входе в конструктор вызвать конструктор предка при помощи зарезервированного слова inherited:

constructor tMyClass.Create;
Begin
inherited Create;
// Код инициализации tMyClass
End;

Как правило, в коде программ, написанных на Delphi, практически н встречается вызовов конструкторов и деструкторов. Дело в том, что любой компонент, попавший при визуальном проектировании в приложение из палитры компонентов, включается в определенную иерархию. Эта иерархия замыкается на форме (класс tForm): для всех ее составных частей конструкторы и деструкторы вызываются автоматически, незримо для программиста. Кто создает и уничтожает формы? Это делает приложение (объект с именем Application). В файле проекта (с расширением DPR) вы можете увидеть вызовы метода Application.CreateForm, предназначенного для этой цели.

Что касается объектов, создаваемых динамически (во время выполнения программы), то здесь нужен явный вызов конструктора и метода Free.

Свойства

Как известно, существует три основных принципа, составляющих суть объектно-ориентированного программирования: инкапсуляция, наследование и полиморфизм. Классическое правило объектно-ориентированного программирования утверждает, что для обеспечения надежности нежелателен прямой доступ к полям объекта: чтение и изменение их содержимого должно осуществляться посредством вызова соответствующих методов. Это правило называется инкапсуляцией (сокрытие данных). В старых реализациях ООП (например в Turbo Pascal) эта мысль внедрялась только посредством призывов и примеров в документации; в Delphi есть соответствующая конструкция. Пользователь объекта в Delphi может быть полностью отгорожен от полей объекта при помощи свойств.

Обычно свойство определяется тремя элементами: полем и двумя методами осуществляющими его чтение/запись:

type
tMyClass=class(tObject)
function GetaProperty: tSomeType;
procedure SetaProperty(Value: tSomeType);
property aProperty: tSomeType read GetaProperty
write SetaProperty;
end;

В данном примере доступ к значению свойства aProperty осуществляется через вызовы методов GetaProperty и SetaProperty, однако в обращении к этим методам в явном виде нет необходимости: достаточно написать

aMyObject.aProperty:=aValue;
aVarable:= aMyObject.aProperty;

и Delphi откомпилирует эти операторы в вызовы соответствующих методов. То есть внешне свойство выглядит в точности как обычное поле, но за всяким обращением к нему могут стоять вызовы необходимых программисту методов. Например, если есть объект, представляющий собой квадрат на экране, и его свойству “цвет” присваивается значение “белый”, то произойдет немедленная прорисовка, приводящая реальный цвет на экране в соответствие значению свойства.

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

tPropClass=class
fValue: tSomeType;
procedure SetValue(aValue: tSomeType);
property Value:tSomeType read fValue write SetValue;
End;

В этом примере поле fValue модифицируется при помощи метода SetValue, а читается напрямую.

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

tReadOnlyClass=class
property NotChanged:tSomeType read GetNotChanged;
End;

В этом примере свойство доступно только для чтения. Попытка присвоить значение свойству NotChanged вызовет ошибку компиляции.

Свойствам можно присваивать значения по умолчанию. Для этого служит ключевое слово default:

Property Visible:boolean read fVisible write SetVisible default TRUE;

Это означает, что при запуске программы свойство будет установлено компилятором в TRUE.

Свойство может быть и векторным. В этом случае оно выглядит как массив:

Property Points[index:integer]:tPoint read GetPoint write SetPoint;

Для векторного свойства необходимо описать не только тип элементов массива, но также и тип индекса. После ключевых слов read и write должны идти имена соответствующих методов. Использование здесь полей массивов недопустимо. Метод, читающий значение векторного свойства, должен быть описан как функция, возвращающая значение того же типа, что и элементы свойства, и имеющая единственный параметр того же типа и с тем же именем, что и индекс свойства:

function GetPoint(index:integer):tPoint;

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

procedure SetPoint(index:integer; Value:tPoint);

У векторных свойств есть еще одна важная особенность: некоторые классы в Delphi (списки tList, наборы строк tStrings и т.д.) “построены” вокруг одного основного векторного свойства. Основной метод такого класса дает доступ к элементам некоторого массива, а все основные методы являются как бы вспомогательными. Для упрощения работы с объектами подобного класса можно описать подобное свойство с ключевым словом default:

type tMyList=class
property list[Index:integer]:string read Getlist write Setlist; default;
end;

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

var MyList:tMyList
Begin
MyList.list[1]:=’First’; {Первый способ}
MyList.[2]:=’Second’; {Первый способ}
End;

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

О роли свойств в Delphi красноречиво говорит тот факт, что у всех имеющихся в распоряжении программиста стандартных классов 100% полей недоступны и заменены базирующимися на них свойствами. Того же правила следует придерживаться и при разработке собственных классов.

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

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

tNewClass=class(tOldClass);

является потомком или дочерним классом класса tOldClass, называемого предком или родительским классом, и добавляете к нему новые поля методы и свойства.

В Delphi все классы являются потомками класса tObject. Поэтому, если вы строите дочерний класс прямо от tObject, то в определении его можно не упоминать. Следующие два описания одинаково верны:

tMyClass=class(tObject);
tMyClass=class;

Более подробно класс tObject будет рассмотрен ниже.

Унаследованные от класса-предка поля и методы доступны в дочернем классе; если имеет место совпадение имен методов, говорят, что они перекрываются.

Рассмотрим поведение методов при наследовании. По тому, какие действия происходят при вызове, методы делятся на три группы. В первую группу отнесем статические методы, во вторую - виртуальные (virtual) и динамические (dynamic) и, наконец, в третью - появившиеся только в Delphi 4 перегружаемые (overload) методы.

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

type
tFirstClass=class
fData:Extended;
procedure SetData(aValue:Extended);
end;

tSecondClass=class(tFirstClass)
fData:Integer;
procedure SetData(aValue:Integer);
end;

procedure tFirstClass.SetData(aValue:Extended);
Begin
fData:=1.0;
End;

procedure tFirstClass.SetData(aValue:Extended);
Begin
fData:=1;
inherited SetData(0.99);
End;

В этом примере разные методы с именем SetData присваивают значение разным полям с именем fData. Перекрытое (одноименное) поле предка недоступно в потомке. Поэтому два одноименных поля с именем fData приведены только для примера.

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

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



2020-02-03 200 Обсуждений (0)
Создание и уничтожение объектов 0.00 из 5.00 0 оценок









Обсуждение в статье: Создание и уничтожение объектов

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

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

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



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

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

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

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

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

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



(0.007 сек.)