Делегаты на C++ - [2]
> return true;
>}
Классы CStaticDelegateVoid и CMethodDelegateVoid можно использовать непосредственно. Но для пользователя удобнее работать исключительно с интерфейсом IDelegateVoid, не задумываясь о существовании двух различных классов реализации. Поэтому напишем перегруженную функцию NewDelegate, которая будет создавать нужный объект и возвращать пользователю IDelegateVoid*. Её реализация будет выглядеть так:
>IDelegateVoid* NewDelegate(void (*pFunc)()) {
> return new CStaticDelegateVoid(pFunc);
>}
>template ‹class TObj›
>IDelegateVoid* NewDelegate(TObj* pObj, void (TObj::*pMethod)()) {
> return new CMethodDelegateVoid‹TObj› (pObj, pMethod);
>}
Мы уже почти закончили. Осталось написать объектную обёртку над интерфейсом IDelegateVoid, которая будет поддерживать список указателей и определять набор операторов, аналогичных используемым в C# - operator=, operator(), operator+= и operator-=. Для простоты будем использовать стандартный класс std::list для хранения списка указателей.
>#include ‹list›
>class CDelegateVoid {
>public:
> CDelegateVoid(IDelegateVoid* pDelegate = NULL) {
> Add(pDelegate);
>}
> ~CDelegateVoid() { RemoveAll(); }
> bool IsNull() { return (m_DelegateList.size() == 0); }
> CDelegateVoid& operator=(IDelegateVoid* pDelegate) {
> RemoveAll();
> Add(pDelegate);
> return *this;
> }
> CDelegateVoid& operator+=(IDelegateVoid* pDelegate) {
> Add(pDelegate);
> return *this;
> }
> CDelegateVoid& operator-=(IDelegateVoid* pDelegate) {
> Remove(pDelegate);
> return *this;
> }
> void operator()() { Invoke(); }
>private:
> void Add(IDelegateVoid* pDelegate);
> void Remove(IDelegateVoid* pDelegate);
> void RemoveAll();
> void Invoke();
>private:
> std::list‹IDelegateVoid*› m_DelegateList;
>};
Для реализации необходимого набора операторов используются вспомогательные методы Add, Remove, RemoveAll и Invoke. Метод Add добавляет новый указатель IDelegateVoid* в список:
>void CDelegateVoid::Add(IDelegateVoid* pDelegate) {
> if (pDelegate != NULL) m_DelegateList.push_back(pDelegate);
>}
Метод Remove ищет в списке делегат, ссылающийся на заданную функцию, и в случае обнаружения удаляет его:
>void CDelegateVoid::Remove(IDelegateVoid* pDelegate) {
> std::list‹IDelegateVoid*›::iterator it;
> for(it = m_DelegateList.begin(); it!= m_DelegateList.end(); ++it) {
> if((*it)-›Compare(pDelegate)) {
> delete (*it);
> m_DelegateList.erase(it);
> break;
> }
> }
> delete pDelegate;
>}
Метод RemoveAll просто очищает список, удаляя из него все делегаты:
>void CDelegateVoid::RemoveAll() {
> std::list‹IDelegateVoid*›::iterator it;
> for(it = m_DelegateList.begin(); it != m_DelegateList.end(); ++it) delete (*it);
> m_DelegateList.clear();
>}
Наконец, метод Invoke вызывает все функции и методы, на которые ссылаются делегаты из списка:
>void CDelegateVoid::Invoke() {
> std::list‹IDelegateVoid*›::const_iterator it;
> for (it = m_DelegateList.begin(); it != m_DelegateList.end(); ++it) (*it)-›Invoke();
>}
Использовать полученный класс делегата можно примерно так.
>void Global() {
> std::cout ‹‹ "Global" ‹‹ std::endl;
>}
>class C {
>public:
> void Method() { std::cout ‹‹ "Method" ‹‹ std::endl; }
> static void StaticMethod() { std::cout ‹‹ "StaticMethod" ‹‹ std::endl; }
>};
>int main() {
> C c;
> CDelegateVoid delegate = NewDelegate(Global);
> delegate += NewDelegate(&c, &C::Method);
> delegate += NewDelegate(C::StaticMethod);
> delegate(); >// вызывается Global, Method и StaticMethod.
> delegate -= NewDelegate(C::StaticMethod);
> delegate -= NewDelegate(Global);
> delegate(); // вызывается только Method.
> return 0;
>}
Как видим, класс CDelegateVoid очень похож на делегаты из C#. Он полностью типобезопасен, так как попытка передать функции NewDelegate ссылку на функцию или метод, сигнатура которых отличается от void(void), немедленно приведёт к ошибке. Реализация класса CDelegateVoid не использует никаких предположений о размере и устройстве указателя на метод класса, поэтому он может использоваться как при обычном, так и при множественном и виртуальном наследовании. Его можно без изменений переносить на новые платформы и компиляторы.
Общая реализация
Теперь посмотрим, как можно обобщить класс CDelegateVoid для применения с различными сигнатурами. Используя шаблоны, мы можем параметризовать как тип возвращаемого значения, так и типы параметров функций, на которые ссылаются делегаты. В то же время, мы не можем определить единый шаблон, поддерживающий разное количество параметров, поэтому для каждого количества параметров необходимо реализовать свой класс. Поскольку наборы от 0 до 10 параметров покрывают 99% практических нужд при работе с делегатами, нам нужно написать 11 шаблонов делегатов CDelegate0, CDelegate1,…, CDelegate10. Вот как будет начинаться описание делегата, который возвращает произвольное значение и принимает произвольный (но ровно 1) параметр.
>template‹class TRet, class TP1›
>class IDelegate1 {
>public:
> virtual ~IDelegate1() {}
> virtual TRet Invoke(TP1) = 0;
> virtual bool Compare(IDelegate1‹TRet, TP1›* pDelegate) = 0;
>};
>template‹class TRet, class TP1›
>class CStaticDelegate1: public IDelegate1‹TRet, TP1› {
>public:
> typedef TRet (*PFunc)(TP1);
> CStaticDelegate1(PFunc pFunc) {
JavaScript еще никогда не был так прост! Вы узнаете все возможности языка программирования без общих фраз и неясных терминов. Подробные примеры, иллюстрации и схемы будут понятны даже новичку. Легкая подача информации и живой юмор автора превратят нудное заучивание в занимательную практику по написанию кода. Дойдя до последней главы, вы настолько прокачаете свои навыки, что сможете решить практически любую задачу, будь то простое перемещение элементов на странице или даже собственная браузерная игра.
В этой книге автор, сам прошедший путь от разработчика до менеджера в сфере IT, рассказывает неочевидные моменты, которые являются критически важными для правильного управления. Почему разработчики увольняются после повышения зарплаты? Как делать FixedPrice проекты? Почему Scrum не упрощает менеджмент? Книга содержит ответ на эти и многие другие вопросы. В книге есть много баек, которые показывают тяжёлую, но интересную жизнь менеджера в разработке. Иллюстратор обложки: Ксения Ерощенко. Иллюстрации в тексте книги авторские.
Что такое ГЕЙМДИЗАЙН? Это не код, графика или звук. Это не создание персонажей или раскрашивание игрового поля. Геймдизайн – это симулятор мечты, набор правил, благодаря которым игра оживает. Как создать игру, которую полюбят, от которой не смогут оторваться? Знаменитый геймдизайнер Тайнан Сильвестр на примере кейсов из самых популярных игр рассказывает как объединить эмоции и впечатления, игровую механику и мотивацию игроков. Познакомитесь с принципами дизайна, которыми пользуются ведущие студии мира! Создайте игровую механику, вызывающую эмоции и обеспечивающую разнообразие.
Эта книга предназначена для всех, кто желает освоить СУБД MySQL. Для ее чтения вам не нужны никакие специальные знания – достаточно быть пользователем Windows. Вы узнаете, как установить и запустить MySQL, как создать собственную базу данных, как работать с данными при помощи команд SQL, как администрировать базу данных и оптимизировать ее работу. Разработчики веб-приложений на языках PHP, Perl и Java найдут в этой книге полезные сведения по использованию базы данных MySQL в соответствующих приложениях.
РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.