<< Предыдущая страница

3.6 Новые возможности языка C++

C++Builder обеспечивает не только поддержку последних нововведении стандарта ANSI C++, но и расширяет язык новыми возможностями. Компоненты, свойства, методы, обработчики событии, а также шаблоны, пространства имен, явные и непостоянные объявления, RTTI и исключения - вся мощь этих средств доступна программистам, использующим C++Builder для визуальной разработки приложений.

Важно понять, что расширения языка никогда не являются самоцелью, и вы по-прежнему сможете компилировать тексты, написанные в рамках стандартного C++. Однако, чтобы воспользоваться в полной мере преимуществами, которые предоставляет C++Builder для технологии быстрой разработки приложений (RAD), вам придется принять введенные расширения языка.

Некоторые из расширений (например, _classid) C++Builder резервирует, главным образом, для внутреннего использования. Другие расширения совершенно очевидны (_int8, _int16 и т.д.), и здесь не рассматриваются. Наше внимание будет сфокусировано на наиболее значимых расширениях C++, которые, в основном, относятся к компонентным классам и будут постоянно встречаться как в тексте книги, так и в ваших приложениях, разрабатываемых в среде C++Builder.

3.6.1 Компоненты

Компоненты часто достигают более высокой степени инкапсуляции, нежели стандартные C++ классы. Проиллюстрируем это на простом примере разработки диалога, содержащего кнопку. В типичной C++ программе для Windows нажатие мышью на кнопку приводит к генерации сообщения WM_LBUTTONDOWN. Это сообщение должно быть "поймано" программой либо в операторе switch, либо в соответствующей строке таблицы откликов (RESPONSE_TABLE), а затем передано процедуре реакции на это сообщение. Так, приложение, написанное в рамках OWL (Object Windows Library), использует макрос

DEFINE_RESPONSE_TABLE1(TEventTestDlgClient, TDialog)
//({TEventTestDlgClientRSP_TBL_BEGIN}}
EV_BN_CLICKED(IDEVENTBUTTON, EventBNClicked);
//({TEventTestDlgClientRSP_TBL_END}}
END_RESPONSE_TABLE;

чтобы ассоциировать событие (сообщение WM_LBUTTONDOWN), генерируемое кнопкой IDEVENTBUTTON в диалоге TEventTestDlgClient, с функцией реакции EventBNClicked.

C++Builder покончил с этими трудно осваиваемыми программистскими трюками. Компонента кнопки уже запрограммирована так, чтобы реагировать на нажатие кнопки событием OnClick. Все, что надо сделать - это выбрать готовый (или написать собственный) метод и с помощью Инспектора объектов включить его в обработчик данного события.

 3.6.1.1 Объявления компонентных классов

Опережающие объявления классов Библиотеки Визуальных Компонент VCL, входящей в состав C++Builder, используют модификатор _declspec:

_declspec(<спецификатор>)

Это ключевое слово может появляться в любом месте перечня объявлений, а не только непосредственно перед модифицируемым объявлением, причем спецификатор принимает одно из следующих значений:

delphiclass используется для опережающего объявления прямых или косвенных производных от VCL класса TObject. Он определяет правила совместимости VCL при обращении с RTTI, конструкторами, деструктором и исключениями.

delphireturn используется для опережающего объявления прямых или косвенных производных от VCL классов Currency, AnsiString, Variant, TDateTime и Set. Он определяет правила совместимости VCL при обращении с параметрами и возвращаемыми значениями функций-членов.

pascalimplementation указывает, что компонентный класс реализован на Объектном Паскале.

VCL класс имеет следующие ограничения:

• Запрещено наследование виртуальных базовых классов.

• Компонентные классы сами не могут служить базовыми классами для наследования.

• Компонентные объекты создаются в динамической памяти кучи с помощью оператора new.

3.6.1.2 Объявления свойств

C++BuiIder использует модификатор _property для идентификации свойств компонентных классов. Синтаксис описания свойства имеет вид:

property <тип свойства> <имя свойства> = {<список атрибутов>} ;

где список атрибутов содержит перечисление следующих атрибутов свойства:

write = < член данных или метод записи > определяет способ присваивания значения члену данных;

read = < член данных или метод чтения > определяет способ получения значения члена данных;

default = < булева константа > разрешает или запрещает сохранение значения свойства по умолчанию в файле формы с расширением .dim;

stored = < булева константа или функция > определяет способ сохранения значения свойства в файле формы с расширением .dfm.

C++BuiIder использует модификатор _published для спецификации тех свойств компонент, которые будут отображаться Инспектором объектов на стадии проектирования приложения. Если разработчик компоненты желает разрешить модификацию значения некоторого свойства, оно не объявляется как _published. Правила видимости, определяемые этим ключевым словом, не отличаются от правил видимости членов данных, методов и свойств, объявленных как public. Единственное отличие проявляется в том, что во время работы программы Инспектору объектов передается информация RTTI.

3.6.1.3 Объявления обработчиков событий

C++BuiIder использует модификатор _closure для объявления функции обработчиков событий:

<тип> (_closure * <name>) (<список параметров>)

Это ключевое слово определяет указатель функции с именем name. В отличие от 4-байтового адресного указателя обычной функции (который передается в кодовые регистры CS:IP) 8-байтовый _closure передает еще и скрытый параметр (непостоянный указатель this на экземпляр текущего класса).

Введение 8-байтовых указателей делает возможным не только вызывать некоторую функцию определенного класса, но и обращаться к функции в определенном экземпляре этого класса. Эта способность была заимствована из Объектного Паскаля, а _closure оказался жизненно необходимым для реализации механизма событий в Библиотеке Визуальных Компонент.

3.6.1.4 Объявления автоматизированных свойств и методов

OLE Automation - это разновидность механизма связи Object Linking and Embedding, позволяющего приложениям для Windows управлять друг другом. Автоматизированный OLE контроллер является приложением, которое способно автоматизировать другое приложение - автоматизированный OLE сервер. По существу, OLE Automation представляет собой протокол обмена, посредством которого контроллер управляет действиями сервера. Все компонентные OLE объекты, экспортируемые автоматизированным сервером своим контроллерам, являются производными от базового класса TAutoObject.

При создании автоматизированного сервера необходимо определить его интерфейс с контроллером, содержащий объявления свойств и методов OLE объекта с тем, чтобы контроллер получил к ним доступ. Никогда не удаляйте уже включенные в интерфейс свойства и методы - это приведет к ошибкам в работе существующих контроллеров. C++Builder использует модификатор _automated в объявлениях автоматизированного метода (Листинг 3.13). Это объявление может заканчиваться еще одним новым ключевым словом _dispid, которое ассоциирует значение идентификатора диспетчеризации OLE Automation с данной функцией.

class MyOLEAutoClass : TAutoObject
{ _automated: void_fastcall function(void) _dispid(1000); };

Листинг 3.13. Объявление автоматизированного метода.

Правила видимости, определяемые этим ключевым словом, не отличаются от правил видимости, объявленных в секции public. Единственное отличие проявляется в том, что генерируемая компилятором информация о типах свойств и методов OLE Automation, делает возможным создание автоматизированных серверов.

C++Builder поставляется вместе с примером (удивительным по внешней простоте и лаконичности кода) взаимодействия приложений контроллера и сервера посредством механизма OLE Automation. Этот пример проливает свет на данную методику, изложенную в системной документации весьма сбивчиво и туманно. Автоматизированный сервер Autosrv демонстрирует использование:

• компонентных объектов TAutoObject и TAutoClassInfo;

• метода RegisterAutoClass автоматизированного объектного класса для регистрации сервера;

• свойств и методов, объявленных с ключевым словом _automated. Контроллер Autocon управляет сервером и демонстрирует:

• установку и выборку свойств объекта сервера;

• использование вариантов (детальная информация об типе Variant содержится в параграфе 3.6.1.6 "Расширенные типы данных Delphi".

Чтобы испытать на практике, что дает взаимодействие приложений OLE Automation, выполните следующие действия:

=> По команде главного меню File | Open Project откройте диалог выбора проектов.

=> Войдите в каталог \...\CBuilder\Examples\Apps\Autosrv

=> Выберите проектный файл с именем Autosrv и нажмите кнопку Open.

=> Командой главного меню Run | Run запустите процесс компиляции и сборки автоматизированного сервера.

=> Снова откройте диалог выбора проектов, войдите в каталог \...\CBuilder\Examples\Apps\Autocon, выберите проектный файл с именем Autocon и нажмите кнопку Open.

=> Запустите процесс компиляции и сборки контроллера.

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

Рис. 3.4. Контроллер готовит сообщение и посылает его серверу.

Рис. 3.5. Контроллер принимает сообщение, "обработанные" сервером.

Рис. 3.6. Контроллер снимает старое сообщение с сервера.

Чтобы узнать, как реализовано такое взаимодействие, необходимо разобраться в текстах модулей автоматизированного сервера (Листинг 3.14 и Листинг 3.15) и контроллера (Листинг 3.16 и Листинг 3.17), которые заслуживают того, чтобы привести их целиком, снабдив необходимыми комментариями. Сервер содержит единственный объект Edit1 компоненты TEdit для редактируемого ввода и приема сообщений от контроллера в свойство Text. Контроллер записывает введенное пользователем сообщение в свойство Text своего объекта Edit1, а управляет сервером посредством трех кнопок Button1, Button2 и Button3 компоненты TButton (с названиями "Послать", "Принять" и "Очистить").

#ifndef Auto2H
#define Auto2H
#include <Classes.hpp>
#include <0leAuto.hpp>
#include <System.hpp>

// Класс сервера ButtonServer. производный от TAutoObjiect
class
ButtonServer : public TAutoObject {

private: // Приватные свойства и методы
    AnsiString _fastcall GetEditStr() ;
    void _fastcall SetEditStr(AnsiString NewVal);
    int _fastcall GetEditNum() ;
    void _fastcall SetEditNum(int NewVal); 

_automated: // Автоматизированные свойства и методы
   property AnsiString EditStr = // свойство типа AnsiString с атрибутами функции чтения/записи значении {read=GetEditStr, write=SetEditStr} ;
   property int EditNum = // свойство типа int с атрибутами функций чтения/записи значений {read=GetEditNum, write=SetEditNum);
    void _fastcall Clear (); // метод очистки сообщения
    void_fastcall SetThreeStr(AnsiString si, AnsiString s2, AnsiString s3); // метод составления текстового
// сообщения из строк в параметрах s1, s2, s3 типа AnsiString
    void _fastcall SetThreeNum(int n1, int n2, int n3); // метод составления текстового сообщения из
// чисел в параметрах п 1, п2, п3 типа int

public: // Общедоступные свойства и методы
   __fastcall ButtonServer(); // конструктор объекта сервера

};

//----------------------------------------------------------

#endif

Листинг 3.14. Файл объявлений Auto1.h модуля автоматизированного сервера.

#include <vcl.h>
#pragma hdrstop
#undef RegisterClass
#include "Auto2.h"
#include "Auto1.h"
int Initialization();
static int Initializer = Initialization();
// Создание объекта автоматизированного сервера _
fastcall ButtonServer::ButtonServer() : TAutoObject()
    { }

// Чтение текстового значения автоматизированного свойства
AnsiString _fastcall ButtonServer::GetEditStr() { return Form1->Edit1->Text;};

// Запись текстового значения автоматизированного свойства
void _fastcall
ButtonServer::SetEditStr(AnsiString NewVal) { Form1->Edit1->Text = NewVal;}

// Чтение численного значения автоматизированного свойства
int _fastcall ButtonServer::GetEditNum()
{
    int val;
    sscanf(Form1->Edit1->Text.c_str(), "%d", &val) ;
    return val;
}

// Запись численного значения автоматизированного свойства
void _fastcall
ButtonServer::SetEditNum(int NewVal)
{
    Form1->Edit1->Text = NewVal;
}

// Очистка значения автоматизированного свойства
void _fastcall ButtonServer::Clear()
{
    Form1->Edit1->Text = "";
}

// Составление текстового значения свойства из трех строк
void _fastcall ButtonServer::SetThreeStr (AnsiString si, AnsiString s2, AnsiString s3)
{
   Form1->Edit1->Text = s1 + ", " + s2 + ", " + s3;
}

// Составление текстового значения свойства из трех чисел

void _fastcall ButtonServer::SetThreeNum (int nl, int n2, int n3)
{
    AnsiString s1(n1), s2(n2), s3(n3);
    Form1->Edit1->Text = s1 + ", " + s2 + ", " + s3;
}

// Регистрация объекта автоматизированного сервера
void _fastcall
RegisterButtonServer()
{
    TAutoClassInfo AutoClassInfo;

// Инициализация полей структуры типа TAutoClassInfo
   AutoClassInfo.AutoClass = _classid(ButtonServer) ;
   AutoClassInfo.ProgID = "BCBAutoSrv.EditServer" ;
   AutoClassInfo.ClassID ="{61E124E1-C869-11CF-9EA7-OOA02429B18A}";
   AutoClassInfo.Description ="Borland C++Builder AutoSrv Example Server Class";
   AutoClassInfo.Instancing = acMultiInstance;

// Регистрация класса автоматизированного сервера
   Automation->RegisterClass(AutoClassInfo) ;
}

// Инициализация объекта автоматизированного сервера

int Initialization()
{
   RegisterButtonServer() ;
   return 0;
}

Листинг 3.15. Кодовый файл Auto2.cpp модуля автоматизированного сервера.

#ifndef Auto1H
#define Auto1H 

class TForm1 : public TForm { 

published: // IDE-managed Components
   TEdit *Edit1;
   TButton *Button1;
   TButton *Button2;
   TButton *Button3;
   TLabel * Label 1;

   void _fastcall Button1Click(TObject *Sender) ;
   void _fastcall Button2Click(TObject * Sender);
   void _fastcall Button3Click(TObject * Sender); 

private: // User declarations
   Variant AutoServer; 

public: // User declarations
    virtual _fastcall TForm1(TComponent *0wner) ; 

};

extern TForm1 *Form1;

#endif

Листинг 3.16. Файл объявлений Auto1.h, все строки которого (за исключением выделенной строки объявления варианта) C++Builder генерирует автоматически при размещении компонент на форме контроллера.

#include <vcl.h>
#pragma hdrstop
#include "auto1.h"
#pragma resource "*.dfm"
Form1 *Form1;

__fastcall TForm1::TForm1(TComponent *0wner) : TForm(Owner)
{
    try { // Создание автоматизированного сервера как объекта OLE
      AutoServer = CreateOleObject("BCBAutoSrv.Edit Server");
   } catch (...) { // Обработка исключения
      ShowMessage("Сначала компилируйте и запустите AutoSrv");
      Application->Terminate() ;
}

// Обработчик события при нажатии кнопки Buttoni

void _fastcall TForm1::Button1Click(TObject *Sender)
{ // Установка автоматизированного свойства сервера
    AutoServer.OlePropertySet("EditStr", Edit1->Text);
}

// Обработчик события при нажатии кнопки Button2

void _fastcall TForm1::Button2Click(TObject *Sender)
{ // Чтение автоматизированного свойства сервера
    Edit1->Text = AutoServer.OlePropertyGet("EditStr");
}

// Обработчик события при нажатии кнопки Button3

void _fastcall TForm1::Button3Click(TObject *Sender)
{ // Очистка автоматизированного свойства сервера
    AutoServer.OleProcedure("Clear");
}

Листинг 3.17. Кодовый файл Auto1.cpp модуля контроллера.

3.6.1.5 Быстрый вызов функций

При объявлении функций, параметры которых передаются через процессорные регистры, используется модификатор __fastcall:

<возвращаемый тип> __fastcall <name>(<список параметров>)

Это ключевое слово определяет, что первые три типизированных параметра функции с именем name (слева направо по списку) передаются не через стек, а через процессорные регистры AX, BX и DX. Регистры не используются, если значение параметра не умещается в регистр, т.е. при передаче через параметр чисел с плавающей точкой, структур и функций.

Строго говоря, быстрый вызов функций не является прерогативой компилятора C++Builder. В предыдущей главе я уже обращал внимание читателя на использование __fastcall в объявлениях функций обработки событий, которые C++Builder генерирует автоматически.

<< Предыдущая страница