Структура объекта, RTTI, операторы Is и As, иерархия классов Delphi


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

Внутреннюю структуру объекта класса

Первое поле каждого экземпляра класса хранит указатель на свой класс, точнее на начало специальной 76-байтной структуры (Delphi 5), называемой RTTI (Run-Time Type In-formation) — генерируемой транслятором описание класса, содержащее информацию времени выполнения о типе. RTTI предоставляет приложениям Delphi возможность получения информации об объектах непосредственно во время выполнения программы.

Формат RTTI — это запись, поля которой:
• Указатель на таблицу VMT (или Nil);
• Указатель на таблицу интерфейсов (или Nil);
• Указатель на таблицу информации о типе (или Nil);
• Указатель на таблицу определения статических методов (или Nil);
• Указатель на DMT (или Nil);
• Указатель на строку с именем класса;
• Адрес метода AfterConstruction и т.п.

В RTTI содержатся данные, полностью характеризующие класс, а класс TObject включает несколько методов, предназначенных для получения информации о конкретном экземпляре объекта: его имя (ClassName), размер экземпляра (In-stanceSize), указатели на класс-предок (ClassParent), тип класса (ClassType) т.п. Одно из полей (со смещением -76) содержит адрес VMT, а другое (со смещением -48) – адрес DMT.

Адреса виртуальных методов в VMT расположены последовательно, в порядке объявления методов в иерархии класса. Первоначально располагаются адреса методов, объявленных в текущем классе, а затем унаследованных.

Адреса динамических методов, объявленных в данном классе, имеют отрицательные индексы (смещения) относительно таблицы VMT. Таблица DMT хранит адреса только тех методов, которые объявлены в данном классе.

После указателя на класс располагаются поля данных класса, начиная с предков. Последними располагаются поля данных текущего класса. Генерирование информации для RTTI осуществляется с ключом $М+. RTTI играет самостоятельную важную роль и может использоваться явно и неявно. В ОР определены два оператора Is и As, неявно использующие RTTI.

Оператор Is (проверка типа)

Оператор Is предназначен для проверки совместимости по присвоению экземпляра какого-нибудь класса с заданным классом. Выражение вида:

If AnObject Is TClassType Then TClassType(AnObject).<метод>;

приводит к выполнению <метода> класса TClassType тогда, когда выражение AnObject Is TClassType принимает значение True. А это происходит только в том случае, если объект AnObject совместим по присвоению с классом TClassType, т.е. является экземпляром этого класса или одного из порожденных от него классов-потомков.

Оператор As (Приведение типа)

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

With ASomeType As TAnotherType Do

приводит к тому, что сам экземпляр остается неизменным, однако вызываются те методы, как если бы он принадлежал к присваиваемому типу. Для несовместимых типов эта операция приводит к возбуждению исключения EInvalidCast.

Напомним, что стандартный (неявный) способ приведения типа выглядит как:

With TAnotherType(ASoineType) Do

От стандартного способа приведения типов использование оператора As отличается наличием проверки на совместимость типов во время выполнения операции приведения типа.

Указатели на класс

Информация в RTTI «живет самостоятельной жизнью» и может использоваться без создания экземпляра класса. Доступ к RTTI класса вне методов класса можно получить, описав соответствующий указатель, называемый указателем (ссылкой) на класс или указателем на объектный тип (Class reference).
Описывается она с помощью зарезервированных слов Class Of

Например, указатель на класс TObject описан в модуле System и называется TClass:

Type TObject=Class;
TClass=Class Of TObject;

Известны также указатели TComponentClass, TControlClass и т.п.

Примечания:
• Переменные — классовые ссылки не несут в себе информации, содержащейся в экземпляре класса, зато содержат информацию о его типе.

• Классовая ссылка используется в следующих случаях:
— когда не известен тип создаваемого объекта на этапе компиляции;
— когда необходим вызов метода класса, чей тип не известен на этапе компиляции:
— в качестве правого операнда в операциях проверки и приведения типов (Is и As).

• Указатели на классы тоже подчиняются правилам приведения объектных типов.

• Ссылки можно использовать во всех выражениях, где допустимо использование типа данных. Например, конструкторы, а также классовые процедуры и функции могут вызываться с помощью ссылки на класс.

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

Type
TMyClass=Class(TObject)
. . .
End;
TObjRef=Class Of TObject;
Var ObjRef: TObjRef;
S: String;
Begin
ObjRef:=TMyClass; // Ссылка на класс, а не на экземпляр
S:=ObjRef.ClassName; // Строка S содержит строку «IMyClass’
. . .
S:=Sender.ClassName;// Строка S содержит имя класса

• Используя классовые ссылки, можно достичь своего рода полиморфизма при создании экземпляров объектов в процессе работы программы.

Type
TMyControl=Class Of TControl;
. . .
Implementation
Function MyCreate(MyClass: TMyControl; Const MyName: String;
X, Y, W, H: Integer): TControl;
Begin
// Создаем новый объект, класс которого MyClass
Result:=MyClass.Create(Form1);
With Result Do Begin
Parent:=Form1; // Parent: =Form1.Panel1; — на другом контейнере
Name:=MyName;
SetBounds(X. Y, W, H);
Visible:=True;
End;
End;
Begin
// Создание поля ввода в нужном месте производится следующим образом
MyCreate(TEdit, ‘Editi’, 10,10,100,20);

• Для динамического удаления компонентов можно воспользоваться свойствами ComponentCount и Components [Index] и оператором Is или использовать функцию FindComponent.

For I:=0 To ComponentCount-1 Do
if (Components[I] Is TEdit Then Components[I].Free;
FindComponent(‘Editi’).Free;
FindComponent(TControl(Sender).Name).Free;

Фактически Delphi использует виртуальный конструктор, создавая компоненты на форме после вашего щелчка по компоненту на палитре компонентов и форме.

Иерархия классов

По мере добавления свойств и методов классы становятся все более специализированными. Начало иерархии классов Delphi, взятое из справочной системы, приведено на следующем рисунке.

Иерархия классов Delphi

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

Пример применения иерархии классов

На каждом уровне иерархии добавляются важные возможности для класса:
TObiect — Включает более 25 методов, общих для всех объектов (17 Delphi 1).
TPersistent — Добавляет возможность писать самого себя в ЕХЕ файл во время компиляции и переносить себя обратно из ЕХЕ файла во время выполнения.
TComponent — Вводит имя компонента (Name), множество состояний объекта (чтение, запись, выборка и т.п.), указатель на владельца (Owner), обеспечивает возможность создания компонентов во время выполнения программы с соответствующей регистрацией компонентов в списке.
TControl — Добавляет возможность взаимодействия с пользователем.
TWinControl — Добавляет возможность использовать механизмы Windows для создания окон.
TButtonControl — Обеспечивает работу с кнопками.
TButton — Реализует «реальные» кнопки с конкретными возможностями: изменение поверхности, опускание кнопки и т.п.


Комментарии запрещены.




Статистика