Размер шрифта
-
+

Обратные вызовы в C++ - стр. 4

А что, если реализовать итератор с помощью набора функций, без использования классов? Интерфейс получается довольно сложным: необходимы отдельные функции для создания итератора, запроса значений, уничтожения итератора; необходимо объявить тип данных для хранения итератора; необходимо предусмотреть уничтожение итератора в случае возникновения исключений.

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


Рис. 5. Просмотр элементов с помощью обратных вызовов


1.2.4. Уведомление о событиях

Представим, что мы в системе запустили таймер, и нам нужно получить уведомление о срабатывании таймера. Самое простое решение – в процессе выполнения опрашивать таймер и анализировать, не истекло ли время. Как часто нужно делать опрос? Слишком часто – теряется производительность, слишком редко – теряется точность. Кроме того, приходится постоянно в определенных участках кода вставлять вызов опроса. Учитывая, что в программе могут работать несколько потоков, опрашивать таймер они будут с разной частотой, и каждый поток обнаружит срабатывание таймера в разное время.

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


Рис. 6. Уведомление о срабатывании таймера с помощью обратного вызова


Итак, мы рассмотрели типовые задачи, в которых используются обратные вызовы. Как видим, подставляя соответствующие аргументы, можно запускать на выполнение различные участки программного кода. Отсюда можно сделать вывод, что обратные вызовы целесообразно использовать в случаях, когда требуется динамическая модификация поведения программы во время выполнения.

1.3. Модель обратных вызовов

1.3.1. Определения и термины

Модель обратных вызовов изображена на Рис. 7. Структурно она состоит из двух частей: исполнитель и инициатор.

Исполнитель – это компонент, в который упаковывается код обратного вызова (исполняемый код). Исполнитель также содержит контекст, который представляет собой совокупность данных, влияющих на поведение исполняемого кода.

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


Рис. 7. Модель обратных вызовов


Дадим формальные определения используемых терминов.

Страница 4