在C++中实现事件(委托)

C++中实现回调机制的几种方式 一文中,我们提到了实现回调的三种方式(C风格的回调函数, Sink方式和Delegate方式)。在面向对象开发中,delegate的方式是最灵活和方便的,因此很早就有人用复杂的模板去模拟(有兴趣的话可以看这里这里),总之是实现起来很复杂。但是现在借助C++11的 function bind , 我们可以很方便的去实现。下面是我自己的一种实现方式:
   namespace  Common
{
    typedef  void* cookie_type;

    template<typename TR, typename T1, typename T2>
     class CEvent
    {
     public:
        typedef TR return_type;
        typedef T1 first_type;
        typedef T2 second_type;

        typedef std::function<return_type (first_type, second_type)> handler_type;

        ~CEvent()
        {
            Clear();
        }

        return_type  operator()(first_type p1, second_type p2)
        {
            return_type ret = return_type();
            size_t size = _handlers.size();
             for(size_t i=0; i<size; ++i)
            {
                ret = _handlers[i]-> operator()(p1, p2);
            }
             return ret;
        }

        cookie_type AddHandler(std::function<return_type (first_type, second_type)> h)
        {
            CEventHandler*p =  new(nothrow)  CEventHandler(h);
             if(p != nullptr) _handlers.push_back(p);
             return (cookie_type)p;
        }

        template<typename class_type, typename class_fun>
        cookie_type AddHandler(class_type* pThis, class_fun f)
        {
            CEventHandler* p =  new(nothrow) CEventHandler(pThis, f);
             if(p != nullptr) _handlers.push_back(p);
             return (cookie_type)p;
        }

         void RemoveHandler(cookie_type cookie)
        {
            CEventHandler* p = (CEventHandler*)cookie;

            auto itr = std::find(_handlers.begin(), _handlers.end(), p);
             if(itr != _handlers.end())
            {
                _handlers.erase(itr);
                delete p;
            }
             else
            {
                assert( false);
            }
        }

         void Clear()
        {
             if(!_handlers.empty())
            {
                 int n = _handlers.size();
                std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler* p)
                { 
                    assert(p != nullptr);
                    delete p;
                });
                _handlers.clear();        
            }
        }

     private:
         class CEventHandler 
        {
         public:
            CEventHandler(handler_type h)
            {
                _handler = h;
                assert(_handler != nullptr);
            }

            template<typename class_type, typename class_fun>
            CEventHandler(class_type* pThis, class_fun object_function)
            {
                 using  namespace std::placeholders;
                _handler = std::bind(object_function, pThis, _1, _2);
                assert(_handler != nullptr);
            }

            return_type  operator()(first_type p1, second_type p2)
            {
                return_type ret = return_type();
                assert(_handler != nullptr);
                 if(_handler != nullptr) ret = _handler(p1, p2);
                 return ret;
            }

            handler_type _handler;
        };


     private:
        std::vector<CEventHandler*> _handlers;
    };
}

大概实现思想是我们通过一个内置的 CEventHandler 类来封装处理函数,我们可以通过AddHandler来添加事件处理函数,添加时会返回一个Cookie,我们可以通过该Cookie来RemoveHandler, 下面是测试代码:
#include "stdafx.h"
#include <iostream>
#include "event1.h"

using  namespace std;

class CObjectX 
{

};

class CClickEventArgs:  public CObjectX
{

};


class CButton:  public CObjectX
{
public:
     void FireClick()
    {
        CClickEventArgs args;
        OnClicked( this, args);
    }

    Common::CEvent< int, CObjectX*, CClickEventArgs&> OnClicked;
};


class CMyClass 
{
public:
     int OnBtuttonClicked(CObjectX* pButton, CClickEventArgs& args)
    {
        cout << "CMyClass: Receive button clicked event" << endl;
         return 1;
    }
};

int OnBtuttonClicked_C_fun(CObjectX* pButton, CClickEventArgs& args)
{
    cout << "C Style Function: Receive button clicked event" << endl;
     return 1;
}


class CMyFunObj
{
public:
     int  operator()(CObjectX* pButton, CClickEventArgs& args)
    {
        cout << "Functor: Receive button clicked event" << endl;
         return 1;
    }
};

int _tmain( int argc, _TCHAR* argv[])
{
     using  namespace std::placeholders;

    CButton btn;

    CMyClass obj;
    Common::cookie_type c1 = btn.OnClicked.AddHandler(&obj, &CMyClass::OnBtuttonClicked);

    Common::cookie_type c2 = btn.OnClicked.AddHandler(OnBtuttonClicked_C_fun);

    CMyFunObj functor;
    Common::cookie_type c3 = btn.OnClicked.AddHandler(functor);

    btn.FireClick();


    btn.OnClicked.RemoveHandler(c2);

    std::cout << endl;


    btn.FireClick();

    system("pause");

     return 0;
}

以下是测试结果:


 可以看到, 我们在普通C函数, 类成员函数和仿函数(functor)中都测试通过。

另外对于事件函数返回值为void的情况,会编译出错,我们需要偏特化一下:
    template< typename T1, typename T2>
     class CEvent< void, T1, T2>
    {
     public:
        typedef  void return_type;
        typedef T1 first_type;
        typedef T2 second_type;

        typedef std::function<return_type (first_type, second_type)> handler_type;

        ~CEvent()
        {
            Clear();
        }

        return_type  operator()(first_type p1, second_type p2)
        {
            size_t size = _handlers.size();
             for(size_t i=0; i<size; ++i)
            {
                _handlers[i]-> operator()(p1, p2);
            }
        }

        cookie_type AddHandler(std::function<return_type (first_type, second_type)> h)
        {
            CEventHandler*p =  new(nothrow)  CEventHandler(h);
             if(p != nullptr) _handlers.push_back(p);
             return (cookie_type)p;
        }

        template<typename class_type, typename class_fun>
        cookie_type AddHandler(class_type* pThis, class_fun f)
        {
            CEventHandler* p =  new(nothrow) CEventHandler(pThis, f);
             if(p != nullptr) _handlers.push_back(p);
             return (cookie_type)p;
        }

         void RemoveHandler(cookie_type cookie)
        {
            CEventHandler* p = (CEventHandler*)cookie;

            auto itr = std::find(_handlers.begin(), _handlers.end(), p);
             if(itr != _handlers.end())
            {
                _handlers.erase(itr);
                delete p;
            }
             else
            {
                assert( false);
            }
        }

         void Clear()
        {
             if(!_handlers.empty())
            {
                 int n = _handlers.size();
                std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler* p)
                { 
                    assert(p != nullptr);
                    delete p;
                });
                _handlers.clear();        
            }
        }

     private:
         class CEventHandler 
        {
         public:
            CEventHandler(handler_type h)
            {
                _handler = h;
                assert(_handler != nullptr);
            }

            template<typename class_type, typename class_fun>
            CEventHandler(class_type* pThis, class_fun object_function)
            {
                 using  namespace std::placeholders;
                _handler = std::bind(object_function, pThis, _1, _2);
                assert(_handler != nullptr);
            }

            return_type  operator()(first_type p1, second_type p2)
            {
                assert(_handler != nullptr);
                 if(_handler != nullptr) _handler(p1, p2);
            }

            handler_type _handler;
        };


     private:
        std::vector<CEventHandler*> _handlers;
    };

最后谈一下在写这个代码中遇到的问题:
(1)不知道你能不能发现下面代码的问题, 我在写代码时就栽在这里了:
      vector < int *>   v;
     int *  p1  =  new  int ( 1 );
     v.push_back(p1);
     int *  p2  =  new  int ( 2 );
     v.push_back(p2);
 
     // 尝试删除所有值为p1的项
    //由该代码想到=>v.erase(std::remove(v.begin(), v.end(), p1), v.end());
     auto itr  =  remove(v.begin(), v.end(), p1);
     for_each(itr, v.end(), []( int *  p){delete p;});
     v.erase(itr, v.end());

(2)我们想把cookei_type放到类里面去, 类似这样:
1      template < typename TR, typename T1, typename T2 >
2      class  CEvent
3      {
4      public :
5          typedef TR return_type;
6          typedef T1 first_type;
7          typedef T2 second_type;
8          typedef  void *  cookie_type;

可发现要这样使用:
Common::CEvent < int , CObjectX * , CClickEventArgs &> ::cookie_type c1  =  btn.OnClicked.AddHandler( & obj,  & CMyClass::OnBtuttonClicked);
太不方便了, 不知道大家有没有好的方法。

注:上面的代码还没有经过真正商业使用,如果有问题欢迎指出。

转载于:https://www.cnblogs.com/weiym/archive/2013/01/31/2886965.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值