Отправляет email-рассылки с помощью сервиса Sendsay

Borland C++ Builder - всякая всячина

  Все выпуски  

Borland C++ Builder всякая всячина (№12 Создание собственных компонентов - продолжение)


Служба Рассылок Subscribe.Ru

Приветствую всех получателей рассылки Borland C++ Builder - всякая всячина!

№12. Создание собственных компонентов - продолжение

 

"...
- Так что, вы говорите, у вас с рукой?
- Подскользнулся, упал, потерял сознание, закрытый перелом, очнулся - гипс...
..."
(к.ф "Бриллиантовая рука")

В прошлом выпуске я начал тему создания собственных компонентов, мы создали кнопку на основе стандартного компонента TSpeedButton. Беда в одном: он ничего особенного не делает и делать не желает. Так что в сегодняшнем и последующих выпусках мы попытаемся прикрутить к нему что-нибудь такое, что придаст смысл проделанной работе. В сегодняшнем выпуске мы просто поподробнее рассмотрим содержимое файлов компонента и особенности компонентной системы вообще.

Для этого сначала поближе рассмотрим исходный файл компонента TMyButton.cpp. Он содержит описания двух функций:

  1. Функция ValidCtrCheck():
    //---------------------------------------------------------------------------
     
    static inline void ValidCtrCheck(TMyButton *)
    {
      new TMyButton(NULL);
    }
    //---------------------------------------------------------------------------

    - используется компилятором для проверки неабстрактности класса компонента. Общий смысл таков: компилятор пробует создать объект этого класса, и если класс имеет хотя-бы один нулевой (абстрактный) метод, компилятор выдает ошибку. Таким образом, вы не сможете создать компонент на основе некоторых базовых компонентов, требующих переопределения "нулевых" методов, например, метода отрисовки объекта на экране Paint. Данная функция получает в качестве аргумента указатель на объект класса TMyButton, поэтому, хоть она и "видна" вне данного модуля, накладок с аналогичными функциями других ваших компонентов быть не должно по причине разницы в аргументах. Еще одно замечание: функция создает объект вашего компонента, но не уничтожает его! Непорядочек... Но! Поскольку явных вызовов этой функции нет нигде (компилятором проверяется только сама возможность создания объекта этого класса), функция не будет включена в конечный файл проекта, никогда не инициируется ее выполнение, она не увеличит размер конечного файла, не будет накладок с созданием и неуничтожением объекта, лепота, в общем. Так что забудте про нее, пусть она присутствует гарантии ради (но и не будет ошибки, если вы ее удалите).

  2. Функция Register():
    //---------------------------------------------------------------------------
     
    namespace Mybutton
    {
      void __fastcall PACKAGE Register()
      {
        TComponentClass classes[1] = {__classid(TMyButton)};
        RegisterComponents("MyPage", classes, 0);
      }
    }
    //---------------------------------------------------------------------------

    - Вот здесь уже все гораздо серьезней: во-первых, данная функция ограничена (на всякий случай) областью видимости данного модуля, поскольку не имеет аргументов, а в других модулях функции с таким же именем могут регистрировать другие ваши компоненты. Во-сторых, ее присутствие реально необходимо, поскольку именно она "объясняет" Билдеру, куда и что поместить (в данном случае, она "прописывает компонент TMyButton на вкладке MyPage). Именно здесь вы можете указать какое угодно имя вкладки. Если вкладка с таким именем уже существует, Билдер будет использовать ее, если нет, он создаст новую с таким именем и зарегистрирует в ней новый компонент. Дальше лезть в дебри реализации этой функции мы пока не будем.

Кроме того, Билдер автоматически влкючил в исходный файл шаблон (заготовку) описания конструктора класса вашего компонента:

//---------------------------------------------------------------------------
 
__fastcall TMyButton::TMyButton(TComponent* Owner)
    : TSpeedButton(Owner)
{
}
//---------------------------------------------------------------------------

Обратите внимение на то, что конструктор получает в качестве параметра ссылку на объект-владелец (Owner) создаваемого объекта (извините за тавтологию). Обычно объектом-владельцем в программе является форма. Она "ведает" созданием и уничтожением всех своих дочерних объектов. Таким образом, если вы "кидаете" вашу кнопку на форму, вы объявляете форму объектом-владельцем данной кнопки. Еще замечание: объект-владелец "ведает" только созданием и уничтожением объекта (то есть, "внутренним", скрытым управлением дочерним объектом), кроме того, для каждого визуального (то есть, отображаемого) компонента, порожденного так или иначе от класса TControl, существует еще и объект-родитель (Parent) любого класса, порожденного от TWinControl, то есть способный иметь дочерние объекты. Он "заведует" визуальной частью управления подчиненным ему объектом, то есть контролирует его отображение, в том числе, предоставляет ему часть своего "холста" (Canvas) на экране и свою координатную систему. Объектом-родителем может быть не только форма-владелец, но и расположенные на этой форме объекты-группировщики: TGroupBox, TPanel, TPageControl и т.д. В этом случае дочерний объект отображается не на форме-владельце, а на родительском объекте-группировщике, использует его холст и его координатную систему.

Теперь перейдем к заголовочному файлу компонента MyButton.h. Там тоже есть на что посмотреть: во первых, обратите внимание, что класс компонента объявлен с использованием макроопределения PACKAGE - пускай будет - это требование Билдера и возведено в ранг закона. Далее, в секции public: присутствует объявление конструктора класса вашего компонента. Необязательно, но очень желательно при создании собственных компонентов там-же объявлять и деструктор того-же класса (в нашем компоненте он точно понадобится): __fastcall ~TMyButton();, а в файл реализации MyButton.cpp - включить его описание:

//---------------------------------------------------------------------------
 
__fastcall TMyButton::~TMyButton()
{
}
//---------------------------------------------------------------------------

Это необходимо для того, чтобы динамически создаваемые в конструкторе объекты уничтожать в деструкторе. И вообще, очень желательно взять за правило: написали слово "new" (в конструкторе) - тут-же напишите в соответствующем месте (в деструкторе) слово "delete" и только потом занимайтесь использованием созданного объекта. Это поможет избежать очень трудноуловимых утечек памяти при выполнении программы. Конструктор и деструктор компонента должны быть объявлены именно в "публичной" секции для того, чтобы к ним могли получить доступ сторонние объекты, например, форма-владелец. Кроме того, в этой секции объявляются все переменные, свойства и методы, доступ к которым производится не из методов данного класса.

Кроме "публичной", открытой части описания класса, в объявлении присутствуют еще несколько разделов:

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

  • protected - раздел для переменных, свойств и методов, доступных только в данном классе или в его потомках. Обычно используется для объявления "заготовок", то есть тех свойств и методов, которые не используются в данном классе, но в порожденных от него классах могут быть вынесены "на всеобщее обозрение". В библиотеке VCL этот подход очень популярен при объявлении различных свойств компонентов.

  • __published - в этой секции объявляются свойства, котрые будут отображены в Object Inspector. Кроме того, в функциях сохранения свойств объекта-компонента на диск или в поток, в работе "участвуют" только свойства, объявленные в этой секции. В остальном, эта секция полностью аналогична секции public.

При выборе секции для объявления чего-либо будте особо внимательны. После того, как ваш компонент уйдет в "свободное плавание" (если уйдет), пользователь не должен гадать, что там внутри - открытый перелом или золото и бриллианты (должен быть доступ ко всем важным свойствам и методам), но в то же время не следует выставлять на всеобщее обозрение всю начинку, снаружи все должно выглядеть красиво и прилично - никаких торчащих хвостов и грубых швов.

На этом я выпуск закончу, следующие 2-3 дня буду более-менее свободен и постараюсь завершить тему...



С уважением, Васильев Евгений...

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog

В избранное