Области видимости элементов класса. Наследование. Виды методов. Указатель Self. Перекрытие виртуальных методов.
Области видимости элементов класса. Наследование. Типы методов. Перегрузка методов.
Области видимости элементов класса.
Delphi предоставляет контроль степени доступа к полям, методам и свойствам объектов с помощью директив private, protected, publicи published, которые записываются в начале соответствующих разделов объявлений.
• Private(закрытый). Объявленные в данном разделе переменные и методы доступны только для того кода, который находится в блоке реализации самого объекта. Директива private скрывает особенности реализации объекта от пользователей и защищает элементы этого объекта от непосредственного доступа и изменения извне.
• Protected(защищенный). Элементы объекта, объявленные в разделе protected, доступны объектам, производным от такого класса. Это позволяет скрыть внутреннее устройство объекта от пользователя и в то же время обеспечить необходимую гибкость, а также эффективность доступа к полям и методам объекта для его потомков.
• Public(открытый). Объявленные в этом разделе члены объекта доступны в любом месте программы. Конструкторы и деструкторы всегда должны быть объявлены как public.
• Published(публикуемый). Для элементов объекта, объявленных в данном разделе, при компиляции будет создана информация о типах времени выполнения (RTTI – Runtime Type Information). Это позволит другим элементам приложения получать информацию об элементах объекта, объявленных как published. B частности, подобная информация используется утилитой Object Inspector при построении списков свойств объектов.
За каждой из этих директив может следовать любое необходимое количество объявлений полей или методов.
Наследование
Все классы Delphi неявно наследуют функциональные возможности базового класса TObject, причем, независимо от того, указано ли это наследование явно или нет. Описание класса TObject расположено в модуле System, и его полезно изучить.
Приведенные ниже два объявления класса являются эквивалентными.
Type
TFoo = class
...
end;
Type
TFoo = class(TObject)
...
end;
У класса TObject имеется довольно много полезных методов (например, любой объект способен сообщить свое имя и тип). Для создания объекта в нем имеется constructor Create, а для уничтожения – destructor Destroy. Кроме того, для уничтожения объектов рекомендуется использовать наследуемый от TObject метод Free, в котором сначала производится проверка, что удаляемый объект существует, а затем вызывается деструктор Destroy.
Наследование позволяет создавать иерархию классов, начиная с некоторого первоначального базового класса (предка) и продолжая более специализированными классами (потомками). Эта иерархия в Delphi может иметь сложную древовидную структуру. Каждый производный класс (потомок, наследник, дочерний класс) несет в себе характеристики своего предка (содержит те же данные, методы и свойства), а также обладает собственными характеристиками. При этом наследуемые данные, методы и свойства описывать у потомка нет необходимости, а использовать можно.
Например, создадим класс TPoint, наследник от класса TInfo:
Type
TPoint = class(TInfo)
x, y: Integer;
constructorCreate(ax, ay: Integer);
procedureShow;
end;
При этом класс TPoint наследует поле FKey и методы Show, GetKey у класса TInfo. Но метод Show у класса TPoint будет свой, т.е. он заменит соответствующий метод класса-предка. Рассмотрим пример использования объектов в программе в случае, когда на этапе проектирования известен тип переменных (статические методы):
...
Var
P: TPoint;
K: TInfo;
Begin
P :=TPoint.Create(200, 305);
K :=TInfo.Create;
K.SetKey(2);
P.Show; // вызывается метод Show для класса TPoint
K.Show; // вызывается метод Show для класса TInfo
...
Тип объекта не всегда известен на этапе разработки программы, иногда он определяется на этапе выполнения, т.е. динамически.
Типы методов
Методы объекта могут быть описаны как статические (static), виртуальные (virtual), динамические (dynamic) или как методы обработки сообщения (message).
Рассмотрим следующий пример:
Type
TMyObject = class(TObject)
procedureIAmAStatic;
procedureIAmAVirtual; virtual;
procedureIAmADynamic; dynamic;
end;
Статические методы
Статический метод IAmAStatic работает подобно обычной процедуре или функции. Этот тип методов устанавливается по умолчанию. Адрес такого метода известен уже на стадии компиляции, и компилятор в коде программы оформляет все вызовы данного метода как статические. Такие методы работают быстрее других, однако не могут быть перегружены в целях полиморфизма объектов.
Виртуальные методы
Метод IAmAVirtual объявлен как виртуальный. Вызов таких методов из-за возможности их перегрузки немного сложнее, чем вызов статического метода, так как во время компиляции адрес конкретного вызываемого метода не известен. Для решения этой задачи компилятор строит таблицу виртуальных методов (VMT – Virtual Method Table), обеспечивающую определение адреса метода в процессе выполнения программы. VMT содержит указатели на все виртуальные методы предка и все виртуальные методы самого объекта, поэтому виртуальные методы используют несколько больший объем памяти, чем методы динамические, однако их вызов происходит быстрее.
Динамические методы
Работа динамического метода IAmADynamic внешне подобна работе виртуальных методов, но их вызов внутренне устроен по-другому. Каждому динамическому методу компилятор назначает уникальное число и использует его вместе с адресом метода для построения таблицы динамических методов (DMT – Dynamic Method Table). В отличие от VMT, DMT содержит указатели лишь на методы данного объекта, благодаря чему обеспечивается экономия используемой памяти, но замедляется вызов метода, поскольку для поиска его адреса, может потребоваться просмотр нескольких DMT в иерархии классов.
Переопределение методов
Переопределение (overriding) метода в Delphi реализует концепцию полиморфизма. Оно позволяет изменять поведение метода от наследника к наследнику. Переопределение метода возможно только в том случае, если первоначально он был объявлен как virtual или dynamic. Для переопределения метода при его объявлении вместо ключевых слов virtual или dynamic следует указать ключевое слово override. Ниже приведен пример переопределения методов IAmAVirtual и IAmADynamic.
Type
TMyObjectChild - class(TMyObject)
procedureIAmAVirtual; override;
procedureIAmADynamic; override;
end;
Директива override приводит к замещению указателя на исходный метод в VMT указателем на адрес нового метода для виртуальных методов, либо созданию новой строки с указателем в DMT при перекрытии динамического метода. Если объявить новые функции с ключевым словом virtual или dynamic, а не override, то вместо замещения указателей на старые методы, будут созданы указатели на новые методы. В случае переопределения статического метода, новый вариант просто заменяет статический метод родителя. У всех виртуальных и динамических методов одной иерархии должны быть не только идентичные имена, но и списки формальных параметров, а для функции также тип возвращаемого значения.
Перегрузка метода
Подобно обычным процедурам и функциям, методы могут быть перегружены таким образом, чтобы класс содержал несколько методов с одним именем, но с различными списками параметров. Перегруженные методы должны быть объявлены с указанием директивы overload
Type
TSampleClass = class
procedureAMethod(I: Integer); overload;
procedureAMethod(S: string); overload;
procedureAMethod(D: Double); overload;
end;