Pull to refresh

Еще раз о делегатах в C++

Reading time4 min
Views8K
После прочтения статьи «Простейший делегат на C++», я понял лишь то, что не понял я абсолютно ничего – наглухо. Может это потому что автор апеллировал в своих мыслях к понятию делегатов в языке C#, который я знаю чуть менее чем никак. Зато я знаю objective C, в котором, делигерование встречается достаточно часто и подход к делигированию там вполне не плохо переностся на C++, а раз у людей на нем пишущих есть потребность в делегатах, то может такой подход им понравится больше. Сразу оговорка – так как в objective C связывание объекта с методом происходит полностью runtime, зделать все так же на плюсах не выйдет, но тут великий Страуструп оставил нам крохотную щель — таблицу виртуальных функций, вот через нее и просочимся.

Для начала потребно разъяснить каков подход к делегированию в objective C.

Итак, по правилам хорошего тона у нас должен быть интерфейс (@interface – некий аналог класса в плюсах, далее назовем его тоже классом), и протокол с объявлением методов-делегатов (@protocol), класс которому нужно установить делегаты должен иметь наследование от этого протокола (говоря языком «кокошников» поддерживать протокол), а класс который вызывает делегаты иметь указатель на класс с методами делегатами и реализованной поддержкой протокола (правда, из-за динамического связывания наследование вовсе не обязательно – просто копипастим имена методов протокола нужному классу, определяем что им делать – компилятор покажет warning, но все работает, а вот на C++ такое неполучится, тут нужно все выше писанное соблюдать ревностно).

Теперь переходим к коду. Для начала объявим пару аналогичную протокол/интерфейс но с поправкой на то что у нас C++;

//файл SomeInterfaceAndProtocol.hpp
#ifndef demo_interface_protocol
#define demo_interface_protocol
struct _protocol //аналог протокола для objC, сам по себе он не юзабилен, но далее используя наследование можно создавать делегаты
{
      void virtual ActionString(char *)=0;//методы делегаты;
      int virtual ActionRetNums(double S)=0;
      int virtual length(char *)=0;
};
class _interface //класс который делигирует
{
      private:
      char *text;
      double flo;
      int lo;
      _protocol *delegate;// собственно главный герой повествования
      public:
       _interface(int Z, double f, char *u);
       _interface(int Z, double f, char *u, _protocol *_);
      void SetDelegate(_protocol *_);
      void action();
      void viewResults();
      ~_interface();
};
#endif


Для наглядности в _interface только имена функций, их алгоритмы обьявятся отдельным файлом, а пока стоит обсудить тот код что выше. Структура _protocol есть наш список методов делегатов, мужно наследовать от нее, а далее делегирующий некие действия интерфейс (назван _interface), люди знающие objective C уже обратили внимание что указатель на класс-делегат имеет определенный тип. Компелятор должен установить связь через таблицу виртуальных функций, а для этого указатель должен иметь тип обьекта с такой таблицей.

Класс _interface включает следуюющие атрибуты: указатель на строку, число целое, число дробное – достаточно чтобы наглядно продемонстрировать как делегат повлияет на эти приватные параметры. А теперь стоит показать что делает наш интерфейс:


#include "SomeInterfaceAndProtocol.hpp"
#include "math.h"
class realprotocol: public _protocol//вот класс, которому мы будем делегировать полномочия
{
 int length(char *text) //пусть просто подщитает длинну строки
 {
     int i=0;
     while(text[i])i++;
     return i;
 }
 void ActionString(char *stroka)//пусть этот пример разворачивает строку задом на перед
 {
     int len=this->length(stroka);
     int k=0;
     char Z;
     while(k!=(int)(len/2))
     {

         Z=stroka[k];
         stroka[k]=stroka[len-1-k];
         stroka[len-1-k]=Z;
         k++;
     };
}
 int ActionRetNums(double S)//пусть возвращает целую часть корня
 {
     return (int)sqrt(S);
 }
};


Обратить внимание (!):class realprotocol: public _protocol – писать надо так, если класс обьявить просто class realprotocol, а дальше написать все также – это работать не будет, более того без развратного типоблудия компелятор это даже не соберет, objective C в этом плане более сговорчив (см. выше), но тут есмь свои недостатки (и много).

А теперь самое время предьявить листинг где делигирующий и делегат взаемодействуюют:


#include <cstdlib>
#include "SomeInterfaceAndProtocol.hpp"
#include "realprotocol.hpp"
realprotocol delegat;
_interface delegator=_interface(44, 66.888, "seidal eht rof maj");
int main(int argc, char *argv[])
{
    delegator.SetDelegate(&delegat);
    delegator.action();
    delegator.viewResults();
    system("PAUSE");
    return EXIT_SUCCESS;
}


Консоль с резельтатами:

image

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

Понятно что этот подход не идеален, но C++ вообще не предрасполагает к делегированию, без костылей никак, обьект которому делегируют возможно должен иметь наследование от другог обьекта и добавлять к наследованию протокол может не всегда получится (запрет на примение множественного наследования в проекте, виртуальные функции в других классах родителях). Хотя зачем нам голова? Правильно – придумывать решения, если с наследованием никак — стандарт С++ вполне позволяет писать такое:


class externalclass:someclass1, someclass2
{
private:
 	class internalclass:public _protocol
{
//бла-бла-бла
};
internalclass forDelegate;
public:
_protocol* GetForDelegate()
{
return &forDelegate;
}

};


Внешний и внутренний класс можно так крепко сцепить, что их взаимодействие даст все что нам необходимо.

РЕЗЮМЕ:

1) Нужно создать для класса который должен делегировать, класс-протокол с виртуальными функцими делегатами и работать с обьектом делегируемым через указатель.
2) Делегируемый класс должен быть унаследован от протокола.
Tags:
Hubs:
-10
Comments27

Articles

Change theme settings