Маніпуляції з методами класів, Різне, Програмування, статті

В якості негативних сторін бібліотеки VCL часто називається її дельфийское походження, що призводить до того, що по деяких моментів поведінку об’єктів VCL-класів не відповідає стандарту С + +. До таких моментів зокрема можна віднести своєрідний порядок виклику конструкторів базових “дельфійних” класів, поведінка віртуальної функції при виклику її в тілі конструктора, обмеження, що накладаються при використанні множинного спадкування (до появи С + + Builder 6 розмова велася не просто про обмеження, а про неприпустимість застосування множинного успадкування для VCL-класів).


Крім того, в компілятор, для підтримки VCL-бібліотеки, були додані розширення, що то ж не вітається прихильниками чистоти C + +. До одних з цих відноситься введення ключового слова __ closure – покажчика на метод класу. На відміну від передбаченого стандартом С + + покажчика на метод класу, __ closure крім самого адреси метод, зберігає ще й адресу екземпляра класу і фізично являє собою структуру, складається з двох покажчиків: на екземпляр класу і на метод класу. Таким чином, __ closure практично є покажчиком не просто метод класу, а на метод об’єкта (примірника) класу.


Не варто думати, що застосування покажчика на метод об’єкта вузько обмежена лише областю VCL класів. Подібні покажчики, хоча й не часто, зустрічаються в программістка практиці і виявляються досить корисними. Є реалізації таких покажчиків з використанням стандартного С + +. [Александреску А. Сучасне проектування на С + +, М.Іздательскій дім «Вільямс», 2002.] В С + + Builder програміст отримує ці можливості даром, в якості своєрідної компенсації за «моральні збитки» в результаті втрати сумісності зі стандартом.


Для ілюстрації цих можливостей створимо простий проект, що складається з однієї форми. На форму покладемо п’ять кнопок і відредагуємо їм Caption відповідно до завдань, які спробуємо продемонструвати.


Ось програма-мінімум, яку ми повинні виконати:



  1. виклик звичайної функції як методу як обробника події;
  2. виклик методу як звичайної функції;
  3. виклик опублікованого (published) методу за його символьному імені;
  4. отримання імені опублікованого методу.

Відповідно до цих завдань наша форма прийме наступний вигляд:




Рисунок 1. Зовнішній вигляд форми проекту.

При натисканні самої верхньої кнопки буде викликати метод Button1Click звичайним стандартним способом, при натисканні інших чотирьох кнопок будуть виконуватися дії, що відповідають поставленим задачам.


Перш за все, визначимо метод і функцію, з якими будемо експериментувати.


Для визначення методу просто поставимо обробник OnClick для верхньої кнопки Button1 і в створеному шаблоні наберемо код тіла методу:






//—————————————————————————
void __fastcall TForm1::Button1Click(TObject *Sender)
{ ShowMessage (AnsiString (“Метод:”) +
this->Name + “->” + ((TComponent*)Sender)->Name);
}
//—————————————————————————

Даний метод при виклику просто буде видавати повідомлення, що метод класу і повідомляти ім’я кнопки, яка була натиснута в момент виклику.


Тепер визначимо звичайну функцію GlobalClick:






//—————————————————————————
void __fastcall GlobalClick(void* This, TObject *Sender)
{ ShowMessage (AnsiString (“Функція:”) +
((TComponent*)This)->Name + “->” +
((TComponent*)Sender)->Name);
}
//—————————————————————————

Функція видаватиме ознака виклику функції і аналогічно методу повідомляти ім’я кнопки, яка була натиснута в момент виклику ..


Зазначимо таку особливість: у функції на відміну від методу додався ще один параметр типу void *. Через цей параметр буде передаватися покажчик на об’єкт класу. В нашому випадку, через нього буде передаватися покажчик на Form1.


Тепер спробуємо організувати виклик звичайної функції в якості обробника події OnClick кнопки Button2.


Зауваження. Події (events) реалізуються в С + + Builder допомогою покажчиків на метод об’єкта (__closure), тобто події є, по суті, покажчиками на метод об’єкта


Цю операцію виконаємо в тілі конструктора форми:






//—————————————————————————
_fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TMethod Method;
Method.Data = this;
Method.Code= GlobalClick;
Button2->OnClick = *(TNotifyEvent*)&Method;
}
//—————————————————————————

При виконанні цієї операції задіяна структура TMethod. Структура задоволена проста і містить всього лише два поля Data і Code, які мають тип void *






struct TMethod
{
void *Code;
void *Data;
};

і за розміром відповідає вказівником на метод об’єкта, що складається, як уже згадувалося вище, також з двох покажчиків. Отже, за допомогою цієї структури за допомогою перетворення можна ініціалізувати покажчик на метод потрібним значенням. Для цього попередньо в поле Data заноситься адреса об’єкту (в даному випадку це форма), в полі Code – адреса функції. Після присвоєння вмісту Method події OnClick дана подія буде пов’язано з функцією, і при натисканні на кнопку Button2 в процесі роботи програми механізм виконання подій викличе функцію GlobalClick і передасть їй в якості параметрів адресу на Form1 та адресу на об’єкт, що запустив подія. В нашому випадку цим об’єктом буде кнопка Button2. Результат роботи програми показаний на малюнку 2:




Рисунок 2. Результат роботи програми.

Виклик методу як звичайної функції виконаємо в тілі обробника кнопки Button3:






//—————————————————————————
void __fastcall TForm1::Button3Click(TObject *Sender)
{ / / Викликати метод як звичайну функцію
TNotifyEvent Click = &Button1Click;
TMethod Method = *(TMethod*)&Click; / / Через перший прихований параметр передаємо this
typedef void (__fastcall *Func)(void*,TObject *);
Func func;
func = (Func)Method.Code;
func(this, Sender);
}
//—————————————————————————

Дана операція виконується в зворотному порядку. Змінної Method присвоюється вміст покажчик на метод. Потім поле Code наводимо до типу: покажчик на функцію параметрами типу void *, TObject * і виконуємо виклик функції, передавши їй в якості параметрів this і Sender. Результат роботи програми можна побачити на малюнку 3.




Рисунок 3. Результат роботи програми.

Для виконання двох інших завдань буде потрібно задіяти додаткові можливості розширеного RTTI, які дісталися у спадок від Delphi і реалізовані у вигляді методів клас TObject. Природно, ця додаткова інформація доступна тільки для VCL-класів, тобто тільки тих класів, які є похідними від TObject. Нам потрібні поки тільки два методи. Це






void * __fastcall MethodAddress(const ShortString &Name);

і






ShortString __fastcall MethodName(void *Address);

За допомогою першого з них можна отримати адресу методу за його символьному імені, за допомогою другого – отримати ім’я, знаючи адресу. При цьому треба враховувати, що використовувати ці функції можна тільки для опублікованих методів, тобто методів, які розміщені в секції __ published.


Виклик методу по імені виконаємо в тілі обробника кнопки Button4, а отримання імені методу в тілі обробника Button5:






//—————————————————————————
void __fastcall TForm1::Button4Click(TObject *Sender)
{
ShortString ProcName = “Button1Click”;
TMethod Method = { MethodAddress(ProcName), this } ;
if (Method.Code)
{
TNotifyEvent Click = *(TNotifyEvent*)&Method;
Click(Sender);
}
}
//—————————————————————————
void __fastcall TForm1::Button5Click(TObject *Sender)
{
TMethod Method = *(TMethod*)&(Button1->OnClick);
ShowMessage( MethodName(Method.Code));
}
//—————————————————————————

Як видно, код досить простий і урахуванням зроблених вище роз’яснень не становить труднощів для розуміння.


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*