Обратные вызовы в C++ - [8]

Шрифт
Интервал

Листинг 9. Инициатор для синхронного обратного вызова с указателем на статический метод класса

>class Executor;

>using ptr_callback_static = void(*) (int, Executor*);


>void run(ptr_callback_static ptrCallback, Executor * contextData = nullptr)

>{

>  int eventID = 0;

>  //Some actions

>  ptrCallback (eventID, contextData);

>}

2.2.5. Преимущества и недостатки

Преимущества и недостатки реализации обратных вызовов с помощью указателя на статический метод класса приведены в Табл. 2.


Табл. 2. Преимущества и недостатки обратных вызовов с указателем на статический метод класса


Простая реализация. Не сложнее, чем для указателей на функцию.

Совместим с инициатором в процедурном дизайне. Можно использовать для работы с системными API.

Инициатор хранит контекст исполнителя. Так же, как и в случае указателей на функцию, усложняет реализацию и способствует увеличению расхода памяти.

2.3. Указатель на метод-член класса

2.3.1. Концепция

В предыдущей главе мы рассматривали использование указателя на статический метод класса, в который в качестве контекста передавали указатель на экземпляр класса. А почему бы нам напрямую не вызвать метод-член класса, минуя прослойку в виде статического метода, из которого вызывается метод-член класса? Для этого нам понадобятся указатель на класс и указатель на метод.

Графическое изображение обратного вызова с помощью указателя на метод-член класса (далее – метод класса) представлено на Рис. 12. Исполнитель реализуется в виде класса, код упаковывается в метод класса, в качестве контекста выступает экземпляр класса. При настройке указатель на метод и указатель на класс как как аргументы сохраняются в инициаторе. Инициатор осуществляет обратный вызов посредством вызова метода, передавая ему требуемую информацию. Контекст здесь передавать не нужно, поскольку внутри метода доступно все содержимое класса.


Рис. 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.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

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


Симуляция частичной специализации

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


Обработка событий в С++

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


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.