
- •3. Объектно-ориентированное программирование (ооп)
- •3.1. Краеугольные камни ооп
- •3.1.1. Что такое объект?
- •3.1.2. Концептуальный пример объекта
- •3.1.3. Природа объекта
- •3.1.4. Понятие класса объектов
- •3.1.5. Три кита ооп
- •3.1.6. Объекты и компоненты
- •3.2. Классы
- •3.2.1. Понятие класса
- •3.2.2. Классы в программных модулях
- •3.3. Объекты
- •3.4. Методы
- •3.4.1. Понятие метода
- •3.4.2. Конструкторы и деструкторы
- •3.5. Свойства
- •3.5.1. Понятие свойства
- •3.5.2. Методы получения и установки значений свойств
- •3.5.3. Свойства-массивы
- •3.5.4. Свойство-массив как основное свойство объекта
- •3.5.5. Методы, обслуживающие несколько свойств
- •3.6. Наследование
- •3.6.1. Понятие наследования
- •3.6.2. Прародитель всех классов
- •3.6.3. Перекрытие атрибутов в наследниках
- •3.6.4. Совместимость объектов различных классов
- •3.6.5. Контроль и преобразование типов
- •3.7. Статические методы
- •3.7.1. Понятие статического метода
- •3.8. Виртуальные методы
- •3.8.1. Понятие виртуального метода
- •3.8.2. Механизм вызова виртуальных методов
- •3.8.3. Абстрактные виртуальные методы
- •3.8.4. Динамические методы
- •3.8.5. Методы обработки сообщений
- •3.9. Указатели на методы объектов
- •3.12. Метаклассы
- •3.12.1. Ссылки на классы
- •3.12.2. Методы классов
- •3.12.3. Виртуальные конструкторы
- •3.13. Классы общего назначения
- •3.13.1. Классы для представления списка строк
- •Свойства:
- •Методы:
- •События:
- •3.13.2. Классы для представления потока данных
- •Общие свойства:
- •Общие методы:
- •3.14. Итоги
- •4. Исключительные ситуации и надежное программирование
- •4.1. Ошибки и исключительные ситуации
- •4.2. Классы исключительных ситуаций
- •4.3. Обработка исключительных ситуаций
- •4.3.1. Создание исключительной ситуации
- •4.3.2. Распознавание класса исключительной ситуации
- •4.3.3. Пример обработки исключительной ситуации
- •4.3.4. Возобновление исключительной ситуации
- •4.3.5. Доступ к объекту, описывающему исключительную ситуацию
- •4.4. Защита выделенных ресурсов от пропадания
- •4.4.1. Утечка ресурсов и защита от нее
- •4.5. Итоги
3.8.3. Абстрактные виртуальные методы
При построении иерархии классов часто возникает ситуация, когда работа виртуального метода в базовом классе не известна и наполняется содержанием только в наследниках. Так случилось, например, с методом Draw, тело которого в классе TFigure объявлено пустым. Конечно, тело метода всегда можно сделать пустым или почти пустым (так мы и поступили), но лучше воспользоваться директивой abstract:
Type TFigure = class procedure Draw; virtual; abstract; end;
|
Директива abstract записывается после слова virtual и исключает необходимость написания кода виртуального метода для данного класса. Такой метод называется абстрактным, т.е. подразумевает логическое действие, а не конкретный способ его реализации. Абстрактные виртуальные методы часто используются при создании классов-полуфабрикатов. Свою реализацию такие методы получают в законченных наследниках.
3.8.4. Динамические методы
Разновидностью виртуальных методов являются так называемые динамические методы.
1. При их объявлении вместо ключевого слова virtual записывается ключевое слово dynamic.
2. В наследниках динамические методы перекрываются так же, как и виртуальные — с помощью зарезервированного слова override.
3. По смыслу динамические и виртуальные методы идентичны. Различие состоит только в механизме их вызова. Методы, объявленные с директивой virtual, вызываются максимально быстро, но платой за это является большой размер системных таблиц, с помощью которых определяются их адреса. Размер этих таблиц начинает сказываться с увеличением числа классов в иерархии.
4. Методы, объявленные с директивой dynamic вызываются несколько дольше, но при этом таблицы с адресами методов имеют более компактный вид, что способствует экономии памяти. Таким образом, программисту предоставляются два способа оптимизации объектов: по скорости работы (virtual) или по объему памяти (dynamic).
3.8.5. Методы обработки сообщений
Специализированной формой динамических методов являются методы обработки сообщений. Они объявляются с помощью ключевого слова message, за которым следует целочисленная константа — номер сообщения. Следующий пример взят из исходных текстов библиотеки VCL:
type TWidgetControl = class(TControl) ... procedure CMKeyDown(var Msg: TCMKeyDown); message CM_KEYDOWN; ... end; |
Метод обработки сообщений имеет формат процедуры и содержит единственный var-параметр. При перекрытии такого метода название метода и имя параметра могут быть любыми, важно лишь, чтобы неизменным остался номер сообщения, используемый для вызова метода. Вызов метода выполняется не по имени, как обычно, а с помощью обращения к специальному методу Dispatch, который имеется в каждом классе (метод Dispatch определен в классе TObject).
Методы обработки сообщений применяются внутри библиотеки VCL для обработки команд пользовательского интерфейса и редко нужны при написании прикладных программ.
3.9. Указатели на методы объектов
Мы уже говорили о процедурном типе. Переменная процедурного типа – это указатель на адрес процедуры или функции. Если же мы хотим сделать ссылку не просто на процедуру или функцию, а на метод объекта, то мы должны добавить ключевое словосочетание of object, записанное после прототипа процедуры или функции:
type
TMethod = procedure of object; TNotifyEvent = procedure(Sender: TObject) of object;
|
Переменная такого типа называется указателем на метод (method pointer). Она занимает в памяти 8 байт и хранит одновременно ссылку на объект и адрес его метода (получается, на самом деле 2 указателя). Например:
type TNotifyEvent = procedure(Sender: TObject) of object;
TMainForm = class(TForm) procedure ButtonClick(Sender: TObject); ... end; var MainForm: TMainForm; OnClick: TNotifyEvent |
Тогда можно сделать следующую привязку:
OnClick := MainForm.ButtonClick; |
Используя переменные типа указателя на метод можно описывать события. Рассмотрим пример с использованием ранее описанного класса TFigure.
Type TFigure = class private FOnDraw: TNotifyEvent; public property OnDraw: TNotifyEvent read FOnDraw write FOnDraw; end;
|
и в методе Draw:
procedure TFigure.Draw; begin ... //рисуем фигурку if Assigned(FOnDraw) then FOnDraw(Self); //уведомление о рисовании фигурки end; end; |
Обратите внимание, что вызов метода через указатель происходит лишь в том случае, если указатель не равен nil. Эта проверка выполняется с помощью стандартной функции Assigned, которая возвращает True, если ее аргумент является связанным указателем.
Описанный выше механизм называется делегированием, поскольку он позволяет передать часть работы другому объекту, например, сосредоточить в одном объекте обработку событий, возникающих в других объектах. Это избавляет программиста от необходимости порождать многочисленные классы-наследники и перекрывать в них виртуальные методы. Делегирование широко применяется в среде Delphi. Например, все компоненты делегируют обработку своих событий той форме, в которую они помещены.