1. Лекция №11 для дисциплин: «Прикладное программирование» и «Языки
программирования»
1
Лекция 11 (ч.4)
Синтаксис и программные конструкции Visual C
4. Библиотека визуальных компонент С++Builder
4.1. Общие сведения о библиотеке VCL
Визуальная среда проектирования С++Builder, начиная с 6-ой версии, поддерживает две
библиотеки программных компонент. Visual Component Library (VCL) и Component Library for
Cross Platform (CLX). Библиотека VCL поддерживается во всех версиях Borland Builder и
содержит большой набор компонент, предназначенных для разработки приложений, работающих
под управлением операционных систем семейства Windows. Библиотека CLX является
платформенно-независимой и позволяет разрабатывать приложения, работающие под
управлением операционных систем семейства Windows и Unix. Например, графическое
приложение, разработанное с помощью этой библиотеки, может быть откомпилировано в среде
Builder для работы в операционной системе Windows и откомпилировано в среде Kylix для
работы в графической системе X-Window операционной системы Linux. Следует отметить, что
система Kylix поддерживает лишь ограниченный набор систем семейства Unix. Для систем Unix
существуют другие платформенно-независимые системы, которые поддерживают практически
все операционные системы данного семейства. Одной из таких систем является система
визуального проектирования QT. Кроме того, библиотека CLX не поддерживает некоторых
возможностей, специфичных для приложений Windows, например, технологии ADO, OLE,
COM+. Все эти и некоторые другие технологии поддерживаются библиотекой VCL. Поэтому при
разработке приложений, работающих под управлением операционных систем семейства
Windows, более целесообразно использовать библиотеку VCL. В дальнейшем будем
рассматривать компоненты библиотеки VCL.
Имена большинства компонентов VCL начинаются с буквы T, имена классов
исключительных ситуаций начинаются с буквы E. Компоненты являются дальнейшим развитием
2. Лекция №11 для дисциплин: «Прикладное программирование» и «Языки
программирования»
2
классов и объектно-ориентированного программирования. В отличие от объектов класса
компоненты существуют не только на этапе выполнения приложения, но и на этапе
проектирования приложения. Поведение компонент может различаться на этих двух этапах,
например, форма на этапе проектирования отображается с точечной сеткой и двойной щелчок
левой кнопкой мыши на свободной области формы приводит к переходу в редактор кода. Кроме
того, в дополнение к компонентным данным и компонентным функциям класса в компонентах
появляются свойства и события. Свойства являются дополнительным интерфейсом для доступа к
компонентным данным класса. Они позволяют устанавливать различные характеристики
компонент, такие как название, вид, размер и т.д. Изменение значений свойств приводит к
изменению каких-либо внутренних данных компонента. Значения свойств могут устанавливаться
как на этапе выполнения, так и на этапе проектирования приложения с помощью инспектора
объектов на закладке Properties. Для того, чтобы свойства были доступны в инспекторе объектов
они должны быть опубликованы. Для этого их определение в классе должно быть размещено в
секции __published. Данная секция относится к атрибутам видимости полей класса (private,
protected, public). На этапе выполнения работа со свойствами практически ничем не отличается
от работы с компонентными данными класса. Например:
TestButton->Caption=”New Text”;
if (TestButton->Color==clButton) TestLabel->Color=clBlack;
Необходимо помнить, что свойство - это интерфейс для доступа к полям данных, а не
само поле данных, поэтому некоторые действия, доступные для переменных, для свойств
недоступны. Например, код Form1->Caption+=”*” некорректен, а код Form1->Caption= Form1-
>Caption+”*” корректен.
События вызываются компонентом для сигнализации о каких-либо изменениях,
связанных с состоянием этого компонента. События могут быть вызваны действиями
пользователя, операционной системой или самим компонентом. Например, щелчок кнопкой
мыши на области компонента, уничтожение компонента, срабатывание таймера и т.д. С каждым
событием прикладной программист, использующий компонент, может ассоциировать функцию-
обработчик события. Функция-обработчик должна быть методом какого-либо компонента,
обычно формы. При возникновении события будет вызвана соответствующая функция-
обработчик. Разработчик компонента определяет, какие события будут поддерживаться
компонентом и условия их возникновения. Большинство сообщений операционной системы
Windows оформлены в библиотеке VCL в виде соответствующих событий. Например, сообщения
3. Лекция №11 для дисциплин: «Прикладное программирование» и «Языки
программирования»
3
от клавиатуры, мыши, сообщения Drag-&-Drop, сообщения органов управления Windows и т.д.
Более подробно внутренняя реализация свойств и событий будет рассмотрена в главе 8. Здесь
лишь отметим, что событие на самом деле содержит в себе указатель на функцию обработчик. В
С++Builder определен специальный тип события с помощью ключевого слова __closure. При
этом определяется указатель на метод класса, дополнительно содержащий указатель на
конкретный экземпляр класса. Например, событие TNotifyEvent описывается следующим
образом:
typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);
Параметр Sender присутствует для каждого события и содержит указатель на экземпляр
компонента, для которого произошло событие. Кроме этого параметра события могут содержать
и другие специфичные для себя параметры, отвечающие, например, за код нажатой клавиши. Для
назначения обработчиков событиям на этапе проектирования можно воспользоваться
инспектором объектов (закладка Events). Каждый компонент имеет так называемое событие по
умолчанию (чаще всего OnClick). При двойном щелчке левой кнопкой мыши на компоненте на
этапе проектирования происходит переход в редактор кода для определения кода обработки
этого события. На этапе выполнения событие по умолчанию никак не отличается от остальных
событий компонента. Обработчик события может быть назначен или убран также и на этапе
выполнения. Например, в unit1.h:
__published:
void __fastcall StartClick(TObject *Sender);
void __fastcall StopClick(TObject *Sender);
в unit1.cpp
void __fastcall TForm1::StartClick(TObject *Sender)
{// выполнение какого-либо кода
ButtonStartStop->Caption="stop";
ButtonStartStop->OnClick=StopClick;
}
void __fastcall TForm1::StopClick(TObject *Sender)
{// выполнение какого-либо кода
ButtonStartStop->Caption="start";
ButtonStartStop->OnClick=StartClick;
}
4. Лекция №11 для дисциплин: «Прикладное программирование» и «Языки
программирования»
4
В данном примере для кнопки ButtonStartStop попеременно назначаются различные
обработчики для события OnClick, позволяя тем самым этой кнопке выполнять при нажатии на
нее различный код. Один и тот же обработчик может быть назначен нескольким событиям
различных компонентов. Например, при разработке графической клавиатурной панели, у нас
есть 10 кнопок с соответствующими цифрами. При нажатии на кнопки соответствующая цифра
должна быть добавлена в поле ввода. Поскольку все обработчики события OnCliсk в этом случае
выполняют аналогичный код, целесообразно назначить один обработчик всем кнопкам. Внутри
обработчика определить, для какой именно кнопки было сгенерировано событие, позволяет
параметр Sender. Следует отметить, что Sender имеет тип указателя на базовый класс библиотеки
VCL TObject и может содержать указатель на объект любого производного класса. В нашем
случае указатель на экземпляр класса TButton. Для корректного использования методов и
свойств компонента необходимо выполнить преобразование типов. Для приведения типов можно
использовать статическое или динамическое преобразование типов. Статическое приведение
типов выглядит как обычное приведение типов языка С++ и происходит на этапе компиляции.
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.
5. Лекция №11 для дисциплин: «Прикладное программирование» и «Языки
программирования»
5
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, то программист сам должен позаботиться об удалении компонента и в
6. Лекция №11 для дисциплин: «Прикладное программирование» и «Языки
программирования»
6
необходимом месте программы вызвать оператор 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).