void __fastcall TForm1::ButtonkbClick(TObject *Sender)
{ Edit1->Text=Edit1->Text+((TButton *)Sender)->Caption; }
В этом случае соответствие типов проверяется на этапе компиляции и проверяются только общие формальные правила соответствия. Т. е. Sender, имеющий тип указателя на класс TObject, может быть преобразован к любому производному классу от TObject. В результате нет возможности проверить соответствие типов во время выполнения. Например, Sender может содержать указатель на экземпляр класса TLabel и будет предпринята попытка преобразовать его к TButton. В этом случае при вызове каких-либо методов характерных для класса TButton произойдет ошибка во время выполнения программы, причем компиляции будет пройдена без ошибок. Поэтому предпочтительней использовать динамическое преобразование типов, при котором проверка и преобразование типов происходит на этапе выполнения, а не на этапе компиляции. В C++Builder для динамического преобразования типов можно использовать специальный оператор dynamic_cast, который используется следующим образом
dynamic_cast< T > (ptr),
где T это тип класса, к которому производится преобразование указателя ptr. (T также может быть указателем или void *). При успешном выполнении оператора возвращается указатель, преобразованный к необходимому типу, в случае неудачного приведения типов возвращается NULL.
void __fastcall TForm1::ButtonkbClick(TObject *Sender)
{TButton *TempButton;
TempButton=dynamic_cast<TButton *>(Sender);
If (TempButton) Edit1->Text=Edit1->Text+TempButton->Caption; }
Оператор dynamic_cast доступен только при использовании механизма RTTI. RTTI (Run-Time Туре Identification) это идентификация типов при выполнении программы. По умолчанию механизм RTTI разрешен в C++Builder. Принудительно запретить его можно, указав - RT - при компиляции, и разрешить, указав - RT. Кроме того, для каждого класса можно включить RTTI (если он будет по умолчанию выключен) с помощью ключевого слова __rtti. Механизм RTTI также позволяет проверять, имеет ли объект некоторый определенный тип, или принадлежат ли два объекта одному и тому же типу. Оператор typeid определяет фактический тип аргумента и возвращает указатель на объект класса typeinfo, который этот тип описывает. Кроме того, для этого класса перегружены операции == и!=, которые можно использовать для сравнения типов двух объектов.
Следует отметить, что все экземпляры всех компонент в С++Builder могут быть только динамическими. Т. е. переменная, через которую происходит обращение к компоненту, должна быть указателем на класс. Для всех компонент, помещенных на форму, такие переменные будут созданы автоматически, кроме того, автоматически будет добавлен код для создания и удаления компонент. При создании компонент во время выполнения приложения все эти действия необходимо делать вручную, т. е. определить указатель на соответствующий класс и затем создать экземпляр класса при помощи оператора new. При этом вызывается конструктор соответствующего класса в качестве параметра, которому необходимо передать указатель на компонент владелец. Обычно владельцем является форма, поэтому в любом методе, принадлежащем форме, можно передавать в качестве этого параметра this:
TLabel *MyLabel;
MyLabel=new TLabel(this);
MyLabel->Parent=MyPanel;
Став владельцем, форма становится ответственной за удаление компонента. При своем закрытии она вызовет деструктор для данного компонента автоматически. Если вместо владельца указать NULL, то программист сам должен позаботиться об удалении компонента и в необходимом месте программы вызвать оператор delete для вызова деструктора: delete MyLabel. Свойство Parent отвечает за старший или родительский компонент и подробнее будет рассмотрено ниже. Экземпляры классов, не являющихся компонентами, могут быть созданы как статически, так и динамически. Например:
AnsiString Str1;
Str1=”Static Str”;
AnsiString *Str2;
Str2=new AnsiString();
*Str2=”Dynamic Str”;
ShowMessage(Str1+"\n"+*Str2);
delete Str2;
В С++Builder также существует специальный тип метакласс (metaclass) или ссылка на тип класса, который определяется следующим образом
class PACKAGE TMetaClass;
typedef TMetaClass* TClass;
Можно определить переменную этого типа, которая будет содержать ссылку на любой класс и будет использоваться, например, для вызова статических методов класса. Для стандартных классов доступ к метаклассу можно получить с помощью ключевого слова __classid(ClassName). Например, __classid(TButton).
4.2. Иерархия классов VCL
Все компоненты VCL находятся в иерархии классов, основу которой составляют базовые классы. Каждый базовый класс имеет некоторый набор методов, событий и свойств и специальное назначение. От этих базовых классов затем наследуются уже реальные классы компонентов и их поведение определено базовыми классами-предками, находящимися выше них в иерархии. Базовые классы библиотеки VCL иногда называют абстрактными, однако они не являются абстрактными классами с точки зрения языка С++. Эти классы не имеют чистых виртуальных функций и для них могут быть созданы экземпляры классов. Однако для этих классов нельзя создать полноценно работающие объекты, поскольку в них содержатся лишь общие характеристики, которые затем должны уточняться и переопределяться в классах-потомках. Тем не менее, эти классы полезны в качестве базовых при создании пользовательских компонент, так как содержат общие характеристики, присущие всем классам данного семейства. Кроме того, указателю на базовый класс может быть присвоен адрес любого объекта производного класса. Полная иерархия базовых классов библиотеки VCL представлена на рисунке.

Рис. 4.1. Фрагмент схемы иерархии классов библиотеки VCL
Основой иерархий являются классы TObject, TPersistent, TComponent, TControl, TGraphicControl, TWinControl. TObject является базовым классом для всех порождаемых классов библиотеки VCL, TComponent является базовым классом для всех порождаемых компонент. Невидимые компоненты произведены от класса TComponent. Отображаемые компоненты имеют общего предка TControl, при этом графические компоненты произведены от класса TGraphicControl, а оконные от класса TWinControl. Причем компоненты, инкапсулирующие стандартные органы управления Windows, произведены непосредственно от класса TWinControl, а оригинальные компоненты от класса TCustomControl. Рассмотрим основные базовые классы более подробно.
Класс TObject инкапсулирует общее функциональное поведение для всех классов системы С++Builder, а именно:
- возможность создания, управления и уничтожения экземпляров объекта;
- поддержка информации RTTI об имени и типе объекта;
- поддержка механизма обработки сообщений.
Большинство этих методов предназначены для внутреннего использования системой С++Builder. Часть методов TObject объявлены как статические (с ключевым словом static). Таким образом, к ним можно обращаться, не создавая экземпляр класса, используя лишь имя класса или метакласс. Рассмотрим некоторые часто используемые методы объекта TObject.
ShortString TObject::ClassName(void) - статический метод, который возвращает строку с именем класса.
TMetaClass * TObject::ClassType(void) - статический метод, который возвращает указатель на метакласс.
TMetaClass * TObject::ClassParent(void) - статический метод, который возвращает указатель на метакласс предка (для TObject возвращает NULL).
bool TObject::InheritsFrom(TMetaClass *) возвращает true, если объект является экземпляром указанного в качестве параметра класса или класса потомка. Например:
if (Sender->InheritsFrom(__classid(TWinControl)))
ShowMessage(«Экземпляр потомка TWinControl»);
void TObject::Dispatch (void* Message) - виртуальный метод, посылает сообщение на обработку, вызывая необходимый обработчик. Обработчик ищется по идентификатору сообщения, расположенному в 2-х первых байтах по адресу, переданному в качестве параметра. В классах потомках в качестве параметра передается указатель на структуру TMessage. Сначала просматриваются обработчики в текущем классе, затем, если обработчик не был найден, - в классах предках. Если обработчик так и не был найден, вызывается DefaultHandler.
void TObject::DefaultHandler(void* Message) - виртуальный метод, обработчик сообщения по умолчанию. Для TObject не выполняет никаких действий (для TWinControl вызывает стандартную API-функцию DefWndProc, которая выполняет обработку сообщений Windows по умолчанию).
int TObject::Free(void) вызывает деструктор для уничтожения объекта. Аналогичные действия могут быть выполнены с помощью оператора delete.
Ниже приведен пример, позволяющий вывести диалоговое окно, содержащее иерархию для объекта переданного в качестве параметра Sender.
void __fastcall TForm1::ControlClick(TObject *Sender)
{AnsiString Str;
TClass tcl;
tcl=Sender->ClassType();
while (tcl)
{ Str+=AnsiString(tcl->ClassName())+"\n";
tcl=tcl->ClassParent();
}
ShowMessage(Str);}
Кроме рассмотренных выше методов существуют еще несколько сервисных методов, использующихся в основном при разработке компонентов. Например, FieldAddress, MethodAddress, MethodName, InitInstance, FreeInstance, InstanceSize и т. д. От TObject наследуются в основном простые объекты, которые не являются компонентами и не нуждаются в поточности и присваивании.
Класс TPersistent является предком всех классов, поддерживающих работу со свойствами. Являясь потомками TPersistent, производные классы приобретают способность поточности и присваивания, а именно:
- возможность сохранения и чтения из потока неопубликованных свойств;
- возможность присвоения значений свойствам;
- возможность присвоения данных одного объекта другому.
Запись и чтение из потока производится в формате dfm. Таким образом, под записью/чтением в поток подразумевается возможность сохранения/загрузки значений свойств в файл формы. В общем случае поточность предоставляет классам возможность помещения в память и загрузки из памяти, например, при переносе компонентов через буфер обмена. Несмотря на то, что поточность появляется, начиная с класса TPersistent, используют эту возможность в основном компоненты, т. е. классы, наследованные от TComponent. Большинство методов TPersistent перегружаются в потомках. Рассмотрим методы этого класса.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |


