Всего за 0.01 руб. Купить полную версию
Для наглядности сведем результаты в Табл. 3.
Табл. 3. Вызовы методов по цепочке наследования
Используя рассмотренные способы управления контекстом, можно реализовать довольно изощренную логику обработки и динамически ее изменять в процессе выполнения программы.
2.3.5. Синхронный вызов
Реализация инициатора для синхронного вызова представлена в Листинг 14. В отличие от асинхронного вызова, здесь аргументы не хранятся, а передаются как входные параметры функции.
Листинг 14. Инициатор для синхронного обратного вызова с указателем на метод-член классаclass Executor;
using ptr_method_callback_t = void(Executor::*)(int);
void run(Executor* ptrClientCallbackClass, ptr_method_callback_t ptrClientCallbackMethod)
{
int eventID = 0;
//Some actions
(ptrClientCallbackClass->*ptrClientCallbackMethod)(eventID);
}
2.3.6. Преимущества и недостатки
Преимущества и недостатки реализации обратных вызовов с помощью указателя на метод член класса приведены в Табл. 4.
Табл. 4. Преимущества и недостатки реализации обратных вызовов с помощью указателя на метод-член класса
Гибкость. Управлять контекстом можно тремя способами, подобные возможности отсутствуют в других реализациях.
Отсутствие трансляции контекста. Контекст транслировать не нужно, метод-член имеет полный доступ к содержимому класса.
Сложность. Код получается довольно громоздким и запутанным.
Тип класса должен объявляться в инициаторе. Здесь достаточно только предварительного объявления класса. Полное объявление класса в инициаторе делать необязательно и даже нежелательно, потому что логически это обработчик обратного вызова, то есть он относится к исполнителю и должен быть в нем реализован. Тем не менее, требование предварительного объявления класса ограничивает независимость исполнителя: он может использовать только те типы классов, которые были предварительно объявлены в инициаторе.
Инициатор должен хранить указатель на метод и указатель на класс. Увеличивается расход памяти.
2.4. Функциональный объект
2.4.1. Концепция
С точки зрения C++ функциональный объект это класс, который имеет перегруженный оператор вызова функции7.
Графическое изображение обратного вызова с помощью функционального объекта представлено на Рис. 14. Исполнитель реализуется в виде класса, код упаковывается в перегруженный оператор вызовы функции, в качестве контекста выступает экземпляр класса. При настройке экземпляр класса как аргумент сохраняется в инициаторе8. Инициатор осуществляет обратный вызов посредством вызова перегруженного оператора, передавая ему требуемую информацию. Контекст здесь передавать не нужно, поскольку внутри оператора доступно все содержимое класса.
Рис. 14. Реализация обратного вызова с помощью функционального объекта.
2.4.2. Инициатор
Предварительно необходимо объявить функциональный объект (см. Листинг 15), потому что его объявление должен видеть как инициатор, так и исполнитель.
Листинг 15.Объявление функционального объектаclass CallbackHandler
{
public:
void operator() (int eventID) //This is an overloaded operator
{
//It will be called by server
};
};
Реализация инициатора приведена в Листинг 16.
Листинг 16. Инициатор с функциональным объектомclass Initiator // (1)
{
public:
void setup(const CallbackHandler& callback) // (2)
{
callbackObject = callback;
}
void run() // (3)
{
int eventID = 0;
//Some actions
callbackObject(eventID); // (4)
}
private:
CallbackHandler callbackObject; // (5)
};
В строке 1 мы объявляется класс-инициатор. В строке 2 объявляется функция для настройки вызова, в которую передается ссылка на функциональный объект. Данный объект присваивается переменной-аргументу, объявленному в строке 5. В строке 3 объявлена функция запуска, внутри этой функции в строке 4 производится вызов перегруженного оператора. Как видим, синтаксис вызова перегруженного оператора совпадает с синтаксисом вызова обычной функции.