|
Программирование >> Многопоточная библиотека с принципом минимализма
closure - расширение языка, определенное в некоторых компиляторах void C closure:: *geronimosWork)() = geronimo.*pActivity; вызываем нужную функцию geronimosWorkC); Тип значения, возвращаемого функцией geronimowork, не содержит никакой информации о классе Parrot. Это значит, что в дальнейщем функцию geronimowork можно связать с другим классом. Все, что для этого нужно - тип возвращаемого значения и аргументы указателя на функцию-член. Фактически это расщирение языка представляет собой некую разновидность класса Functor, но его применение офани-чено объектами и функциями-членами (на обычные функции и функторы это расщирение не распространяется). Перейдем к реализации связывания указателей с функциями-членами класса Functor. Опыт работы с функторами и функциями подсказывает, что было бы хорощо остаться на высоком уровне абстракции и до поры до времени пренебречь спецификой. В реализации класса MemFunHandlег тип объекта (в предыдущем примере им был класс Parrot) является шаблонным параметром. Более того, указатель на функцию-член мы также сделаем шаблонным параметром. Это позволит свободно выполнять автоматическое преобразование типов в реализации класса FunctorHandler, которая приводится ниже. В этой реализации воплощены описанные выше идеи, а также некоторые идеи, уже использованные при разработке класса Functor. template <class ParentFunctor, typename PointerToObj) typename PointerToMemFn> class MemHandler : public Functorlmpl < typename ParentFunctor::ResultType, typename ParentFunctor::ParmList > public: typedef typename ParentFunctor::ResultType ResultType; MemFunHandlerCconst PointerToObjA, pointerToMemFn pMemFun) : pObj CpObj), pMemFn CpMemFn) {} MemFunHandler* CloneO const return new MemFunHandlerC*this); } ResultType operatorOO return CC*pObj ) .*pMemFn )0; ResultType operatorO(typename ParentFunctor::Parml pl) return ((*pObj ).*pMemFn )(pl); ResultType operatorO(typename ParentFunctor::Parml pl, typename ParentFunctor::Parm2 p2) return ((*pObj ).*pMemFn )(pl. p2); private: PointerToObj pObj ; PointerToMemFn pMemFn ; Почему параметром шаблонного класса MemFunHandlег является тип указателя (Poi nterToobj), а не тип самого объекта? Более естественно выглядела бы такая реализация. template <class ParentFunctor, typename Obj) typename PointerToMemFn> class MemHandler : public Functorlmpl < typename ParentFunctor::ResultType, typename ParentFunctor::ParmList > private: obj* pObj ; PointerToMemFn pMemFn ; public: MemFunHandlerCobj* pObj, PointerToMemFn pMemFun) : pObj CpObj), pMemFn CpMemFn) {} Понять такой код было бы проще. Однако первая реализация является более обобщенной. Во вторую реализацию встроен тип указатель на объект , причем этот указатель представляет собой просто адрес объекта класса obj и больше ничего. Может ли это создать проблемы? Конечно, если понадобится применить интеллектуальные указатели на объекты класса MemFunHandl ег. Убедились? Первая реализация поддерживает интеллектуальные указатели, а вторая - нет. Первая реализация может хранить любой тип, действующий как указатель на объект, а вторая хранит и использует только простые указатели. Более того, вторая версия не работает с указателями на константы. Вот как проявляются негативные черты жестко встроенных типов. Попробуем протестировать только что созданную реализацию на примере класса parrot. #include Functor.h #include <iostream> using namespace std; class Parrot { public: void EatC) { cout НЯМ, НЯМ, НЯМ ...\n ; void SpeakC) { cout пиастры! пиастры I\n ; int main С) { Parrot geronimo; Определяем два функтора Functoro cmdlC&geronimo, &Parrot::eat), cmd2(&geronimo, &Parrot::Speak); вызываем каждый из них cmdlC); cmd2(); Поскольку класс MemFunHandl ег должен быть как можно более общим, автоматическое преобразование типов выполняется соверщенно свободно - абсолютно так же, как и в классе FunctorHandler. 5.10. Связывание На этом мы могли бы остановиться. Теперь у нас все есть - класс Functor поддерживает все вызываемые сущности языка С++, определенные выще, причем делает это весьма успещно. Однако, как мог убедиться Пигмалион, начиная работу, невозможно предсказать, каков будет ее результат. Поскольку класс Functor уже готов, возникают новые идеи. Например, хотелось бы иметь возможность конвертировать тип Functor в другой тип. Одним из таких преобразований является связывание (binding). Пусть задан класс Functor, получающий целые числа. Мы хотим связать одно из этих целых чисел с некоторым фиксированным значением, а второе оставить переменным. Такое связывание порождает новый класс Functor, получающий только одно целое число, поскольку второе зафиксировано и, следовательно, известно. Описанное выше связывание иллюстрируется следующим примером. void f() { определяем функтор с двумя аргументами Functor<void, TYPELlST 2(int, int)> cmdl(something); Связываем первый аргумент со значением 10 Functor<void, int> cmd2(BindFirstCcmdl, 10)); Эквивалентно cmdlClO, 20) cmd2(20); Затем связываем первый (и только первый) аргумент функтора cmd2 со значением 30 Functor<void> cmd3(BindFirst(cmd2, 30)); Эквивалентно cmdlClO, 30) cmd3 0; Связывание - мощное оружие. Оно позволяет хранить не только вызываемые сущности, но и часть (или все) их аргументы. Это значительно расширяет возможности функторов, поскольку теперь можно упаковывать функции и аргументы, не прибегая к генерации дополнительного кода для их связывания. Например, представьте себе реализацию операции повтора (redo) в текстовом редакторе. Когда пользователь набирает букву а , выполняется функция Document: :lnsertchar(a). Добавим готовый функтор, содержащий указатель на класс Document, функцию-член Insertchar и фактический символ. Если пользователь выполняет в меню пункт Redo, достаточно лишь активизировать функтор - и все. Более подробно операции отката и повтора обсуждаются в разделе 5.14.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |