Рефераты. Программирование на Delphi

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

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

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;

Страницы: 1, 2, 3, 4, 5, 6, 7, 8



2012 © Все права защищены
При использовании материалов активная ссылка на источник обязательна.