Выбрать главу

Рис. 12. Реализация обратного вызова с помощью указателя на метод-член класса

2.3.2. Инициатор

Реализация инициатора приведена в Листинг 10.

Листинг 10. Инициатор с указателем на метод-член класса

class Executor;  // (1)

class Initiator  // (2)

{

public:

  using ptr_callback_method = void(Executor::*)(int);  // (3)

  void setup(Executor* argCallbackClass, ptr_callback_method argCallbackMethod)  // (4)

  {

    ptrCallbackClass = argCallbackClass; ptrCallbackMethod = argCallbackMethod;  // (5)

  }

  void run()  // (6)

  {

    int eventID = 0;

    //Some actions

    (ptrCallbackClass->*ptrCallbackMethod)(eventID);  // (7)

  }

private:

  Executor* ptrCallbackClass = nullptr;             // (8)

  ptr_callback_method ptrCallbackMethod = nullptr;  // (9)

};

В строке 1 делается предварительное объявление типа класса исполнителя. В строке 2 объявляется класс-инициатор, в строке 3 объявляется тип указателя для класса-исполнителя. В строке 4 объявляется функция для настройки указателей, соответствующие переменные (указатель на метод класса и указатель на экземпляр класса) объявлены в строках 8 и 9. В строке 6 объявлена функция запуска, внутри этой функции в строке 7 через соответствующий указатель производится вызов метода класса.

2.3.3. Исполнитель

Реализация исполнителя приведена в Листинг 11.

Листинг 11. Исполнитель с указателем на метод-член класса

class Executor                       // (1)

{

public:

  void callbackHandler(int eventID)  // (2)

  {

    //It will be called by initiator

  }

};

int main()                                                 // (3)

{

  Initiator initiator;                                     // (4)

  Executor executor;                                       // (5)

  initiator.setup(&executor, &Executor::callbackHandler);  // (6)

  initiator.run();                                         // (7)

}

В строке 1 объявляется класс-исполнитель. В строке 2 объявлен метод класса, который будет выполнять функцию обработчика обратного вызова. В указанный метод передается информация вызова (в нашем случае это eventID). В строке 3 объявлена основная функция, в которой осуществляются все необходимые операции. В строке 4 объявлен класс-инициатор, в строке 5 объявлен класс-исполнитель. В строке 6 осуществляется настройка обратного вызова, в строке 7 производится запуск инициатора.

2.3.4. Управление контекстом

Рассматриваемая реализация позволяет осуществлять управление контекстом тремя способами: настройка экземпляра класса-исполнителя, настройка указателя на метод, переопределение виртуальных функций. Это приводит к интересным эффектам.

Пусть у нас будут объявления классов-исполнителей с наследованием, как показано в Листинг 12. Графически иерархия наследования изображена на Рис. 13.

Листинг 12. Классы-исполнители с наследованием

class Executor

{

public:

  virtual void callbackHandler1(int eventID);

  virtual void callbackHandler2(int eventID);

};

class Executor1: public Executor

{

public:

  void callbackHandler1(int eventID) override;

};

class Executor2: public Executor

{

public:

  void callbackHandler2(int eventID) override;

};

class Executor3: public Executor1, public Executor2

{

};

Рис. 13. Иерархия наследования классов-исполнителей

Итак, будем назначать различные указатели на экземпляры классов и методы-члены, как показано в Листинг 13.

Листинг 13. Настройка указателей на классы и методы

int main()

{

  Initiator initiator;

  Executor  executor;

  Executor1 executor1;

  Executor2 executor2;

  Executor3 executor3;

  initiator.setup(&executor, &Executor::callbackHandler1);   // (1)

  initiator.setup(&executor, &Executor::callbackHandler2);   // (2)

  initiator.setup(&executor1, &Executor::callbackHandler1);  // (3)

  initiator.setup(&executor1, &Executor::callbackHandler2);  // (4)

  initiator.setup(&executor2, &Executor::callbackHandler1);  // (5)

  initiator.setup(&executor2, &Executor::callbackHandler2);  // (6)

  //initiator.setup(&executor3, &Executor::callbackHandler1); //Incorrect, base class is ambiguous  // (7)

  //initiator.setup(&executor3, &Executor::callbackHandler2); //Incorrect, base class is ambiguous  // (8)