- •Объектно-ориентированное программирование
- •Часть 1 классы и объекты
- •Введение
- •1. Классы и объекты
- •Var aLine: tLine;
- •Var aColorLine: tColorLine;
- •2. Методы
- •2.1. Методы-функции и методы-процедуры
- •2.2. Конструкторы и деструкторы
- •Inherited Create;
- •Var TmpFrm: tForm;
- •Var Mem: tMemo;
- •2.3. Классовые процедуры и функции
- •Var s: String;
- •2.4. Скрытый Self
- •3. Видимость компонентов класса
- •4. Наследование
- •4.1. Основные понятия
- •4.2. Наследование полей
- •4.3. Поведение методов при наследовании
- •Var SomeObject: t1;
- •Virtual;
- •Virtual; Abstract;
- •4.4. Иерархия классов
- •4.5. Rtti
- •4.6. Проверка типа
- •4.7. Приведение типа
- •4.8. Указатели на класс
- •Var ObjRef: tObjRef;
- •Implementation
- •X, y, w, h: Integer): tControl;
- •5. Полиморфизм
- •6. Свойства (properties)
- •6.1. Объявление свойств
- •6.2. Объявления свойств-массивов
- •Var I: Byte;
- •6.3. Раздел Read
- •6.4. Раздел Write
- •Inherited Create;
- •Inherited Destroy;
- •6.5. Команды Default, NoDefault и Stored
- •6.6. Команда Index
- •Var aYear, aMonth, aDay: Word;
- •Var aYear, aMonth, aDay: Word;
- •6.7. Команды DispId и Implements
- •6.8. Переопределение свойств при наследовании
- •7. События (events)
- •7.1. Объявление событий
- •IfAssigned(fOnMouseMove) Then fOnMouseMove(Self, Shift, X, y);
- •Vk_return: NumMemo.DoNumStr(l);
- •Vk_return: PostMessage(NumMemo.Handle, wm_user1,1, 0);
- •7.2. Обработчики событий
- •7.3. Делегирование событий
- •Var Objl: tIstClass;
- •7.4. Переопределение стандартных событий
- •Var NewBtn: tNewButton;
Inherited Create;
FTitle:=AnTitle;
FFilm:=TFilm.Create; // FFilm: =Nil; если объект пока не нужен End;
Примечания:
• Реализация метода начинается с указания зарезервированного слова Procedure\Function, за которым следует полное имя метода и параметры:
<имя класса>.<имя метода>[(<параметры>)];
Для метода-функции следует указать и <тип результатам.
• Следует убедиться, что для каждого поля в конструкторе предусмотрен оператор присвоения, и что все поля переходят из неопределенного состояния в какое-то конкретное, предусмотренное по умолчанию, хотя и известно, что транслятор сам выполнит инициализацию нулевыми значениями ('', Nil, False - для других полей). Для неклассовых полей такая инициализация вполне допустима, поскольку позволяет их использовать в дальнейшем. Инициализация классового поля значением Nil не позволяет непосредственное использование такого поля, поскольку память для хранения данных не отведена. При попытке обращения к такому полю будет выведено сообщение примерно следующего вида:
• Для вызова наследуемого конструктора следует использовать ключевое слово Inherited, которое фактически обеспечивает доступность перекрытого метода. Сила оператора в том, что он вызывает старое, а затем возвращается к новому.
• Как правило, следует вызывать подходящий наследуемый конструктор в первом исполняемом операторе.
• Только, если у пользовательского класса нет новых полей, можно не создавать для него конструктор.
• В тех случаях, когда по каким-либо причинам не удается инициализировать экземпляр класса полностью, должен быть предусмотрен механизм прекращения создания объекта и возврата к коду, вызвавшему конструктор, проинформировав его об ошибке и установив ссылку на объект в Nil. Ранее для этих целей была предусмотрена предопределенная процедура Fail. Она может использоваться сейчас, но только внутри конструкторов. Однако более прогрессивным считается использование механизма исключений, но о нем позже.
• Хотя в объявлениях конструкторов и не указывается тип возвращаемого результата, и они выглядят, как объявления процедур, конструкторы используются скорее как функции, а не как процедуры. Можно сказать, что конструктор является неявной функцией - он возвращает нового представителя того класса, который использовался при его вызове. Нет необходимости в явном виде задавать тип возвращаемого результата, поскольку этот тип и так известен на момент вызова - это тип использованного в вызове конструктора класса.
• Внутри конструктора отсутствует и явное присвоение возвращаемого значения. Такое возможно, поскольку он всегда возвращает ссылку (адрес) на вновь созданный и инициализированный экземпляр класса.
• Начиная с класса TComponent конструктор Create стал виртуальным и при его переопределении необходимо указывать слово-директиву Override, назначение которого будет пояснено позднее.
т) Реализация деструкторов.
Деструктор уничтожает экземпляр класса, который был использован при его вызове, автоматически освобождая любую динамическую память, которая ранее была зарезервирована конструктором, закрывает файлы и т.п. операции. Деструктор вызывается тогда, когда работа с данным экземпляром класса закончена.
Как и в случае с конструктором нет необходимости заботиться о деталях освобождения динамической памяти уничтожаемого объекта, поскольку это делается автоматически. Программист ответственен за вызов деструкторов для всех экземпляров класса, если были зарезервированы подчиненные объекты.
Синтаксис реализации деструктора:
Destructor <имя класса>.<имя деструктора>[(<параметры>)];
[<блок объявлений>]
Begin
<исполняемые операторы>
End;
Реализация наследуемых деструкторов.
Если использовать механизм наследования деструкторов, то можно упростить задачу уничтожения экземпляров класса, таким образом, чтобы каждый раз заботиться лишь об уничтожении тех полей, которые были добавлены в данном классе. Всю работу по очистке наследуемых полей можно возложить на наследуемые деструкторы. Для вызова наследуемого деструктора необходимо используется ключевое слово Inherited. Синтаксис объявления наследуемого деструктора следующий:
Destructor <имя класса>.<имя деструктора>[(<параметры>)};
[<блок объявлений>]
Begin
<уничтожение собственных полей>
Inherited <имя деструктора>[{<параметры>)];
End;
Destructor TPictureShow-Destroy;
Begin
FFilm.Destroy; // Для полей-ссылок win FFilm.Free;
FTitle:="; // Для обычных полей
Inherited Destroy; // Можно вызвать и наследуемый TObject.Destroy
End;
Примечания:
• Реализация деструктора начинается с указания зарезервированного
слова Destructor, за которым следует полное имя деструктора:
<имя класса>.<имя деструктора>.
• Внутри деструктора есть доступ не только к обычным идентификаторам, но и к полям экземпляра класса, инкапсулированным при его определении.
• Исполняемые операторы деструктора должны позаботиться обо всех операциях очистки, необходимых для уничтожения экземпляра класса. Код деструктора должен уничтожить все внутренние экземпляры объектов и освободить динамическую память, которая была зарезервирована во время существования экземпляра класса. Однако нет необходимости явно устанавливать в нулевые значения поля прямого доступа.
• Следует иметь в виду, что при неудачных попытках создания экземпляров класса деструктор, особенно Destroy, используемый по умолчанию, может быть вызван лишь с частично инициализированным экземпляром, поэтому он должен быть подготовлен к работе с полями, которые еще не
инициализированы.
• Необходимость в объявлении деструкторов с параметрами возникает очень редко. Обычно все, что нужно сделать деструктору - уничтожить экземпляр класса, и вся необходимая для этого информация и так доступна ему.
• Если у класса нет полей косвенного доступа, то деструктор можно не создавать для такого пользовательского класса.
• Фактически нет не наследуемых конструкторов и деструкторов.
е) Вызов конструкторов.
Для того чтобы вызвать конструктор, необходимо правильно объявить и определить класс. Объявление класса должно быть доступно, т.е. он должен находится в области видимости из того места, где конструктор будет вызываться.
Если в пользовательском классе не определен конструктор, то по умолчанию будет использоваться конструктор, унаследованный от класса-потомка. В любом случае у всех объектов есть доступ к конструктору Create, определенному в классе TObject.
Определение класса создает активную структуру, способную создавать представителей этого класса. Объекты, которые создаются с помощью определения класса, способны хранить ссылки на вновь создаваемые объекты.
Синтаксис объявления переменной и вызова конструктора следующий:
Var <имя обьекта>: <имя класса>; // Объявление переменной -указателя
Begin
. . .
<имя обьекта>:=<имя класса>.<имя конструктора>[(<параметры>)];
. . .
End;