zoukankan      html  css  js  c++  java
  • 在C++中实现事件(委托)

    C++中实现回调机制的几种方式一文中,我们提到了实现回调的三种方式(C风格的回调函数, Sink方式和Delegate方式)。在面向对象开发中,delegate的方式是最灵活和方便的,因此很早就有人用复杂的模板去模拟, 实现起来很复杂。但是现在借助C++11的functionbind, 我们可以很方便的去实现。下面是我自己的一种实现方式:
      1 #pragma once
      2 
      3 #include <functional>
      4 #include <algorithm>
      5 #include <vector>
      6 #include <assert.h>
      7 
      8 namespace Common
      9 {
     10     typedef void* cookie_type;
     11 
     12     template<typename TR, typename T1, typename T2>
     13     class CEvent
     14     {
     15     public:
     16         typedef TR return_type;
     17         typedef T1 first_type;
     18         typedef T2 second_type;
     19 
     20         typedef std::function<return_type (first_type, second_type)> handler_type;
     21 
     22         ~CEvent()
     23         {
     24             Clear();
     25         }
     26 
     27         return_type operator()(first_type p1, second_type p2)
     28         {
     29             return_type ret = return_type();
     30             size_t size = _handlers.size();
     31             for(size_t i=0; i<size; ++i)
     32             {
     33                 ret = _handlers[i]->operator()(p1, p2);
     34             }
     35             return ret;
     36         }
     37 
     38         cookie_type AddHandler(std::function<return_type (first_type, second_type)> h)
     39         {
     40             CEventHandler*p = new(nothrow)  CEventHandler(h);
     41             if(p != nullptr) _handlers.push_back(p);
     42             return (cookie_type)p;
     43         }
     44 
     45         template<typename class_type, typename class_fun>
     46         cookie_type AddHandler(class_type* pThis, class_fun f)
     47         {
     48             CEventHandler* p = new(nothrow) CEventHandler(pThis, f);
     49             if(p != nullptr) _handlers.push_back(p);
     50             return (cookie_type)p;
     51         }
     52 
     53         void RemoveHandler(cookie_type cookie)
     54         {
     55             CEventHandler* p = (CEventHandler*)cookie;
     56 
     57             auto itr = std::find(_handlers.begin(), _handlers.end(), p);
     58             if(itr != _handlers.end())
     59             {
     60                 _handlers.erase(itr);
     61                 delete p;
     62             }
     63             else
     64             {
     65                 assert(false);
     66             }
     67         }
     68 
     69         void Clear()
     70         {
     71             if(!_handlers.empty())
     72             {
     73                 int n = _handlers.size();
     74                 std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler* p)
     75                 { 
     76                     assert(p != nullptr);
     77                     delete p;
     78                 });
     79                 _handlers.clear();        
     80             }
     81         }
     82 
     83     private:
     84         class CEventHandler 
     85         {
     86         public:
     87             CEventHandler(handler_type h)
     88             {
     89                 _handler = h;
     90                 assert(_handler != nullptr);
     91             }
     92 
     93             template<typename class_type, typename class_fun>
     94             CEventHandler(class_type* pThis, class_fun object_function)
     95             {
     96                 using namespace std::placeholders;
     97                 _handler = std::bind(object_function, pThis, _1, _2);
     98                 assert(_handler != nullptr);
     99             }
    100 
    101             return_type operator()(first_type p1, second_type p2)
    102             {
    103                 return_type ret = return_type();
    104                 assert(_handler != nullptr);
    105                 if(_handler != nullptr) ret = _handler(p1, p2);
    106                 return ret;
    107             }
    108 
    109             handler_type _handler;
    110         };
    111 
    112 
    113     private:
    114         std::vector<CEventHandler*> _handlers;
    115     };
    116 
    117 } //Common

    大概实现思想是我们通过一个内置的CEventHandler 类来封装处理函数,我们可以通过AddHandler来添加事件处理函数,添加时会返回一个Cookie,我们可以通过该Cookie来RemoveHandler, 下面是测试代码:
     1 // EventTest.cpp : Defines the entry point for the console application.
     2 //
     3 
     4 #include "stdafx.h"
     5 
     6 #include <iostream>
     7 #include "event1.h"
     8 
     9 using namespace std;
    10 
    11 class CObjectX 
    12 {
    13 
    14 };
    15 
    16 class CClickEventArgs: public CObjectX
    17 {
    18 
    19 };
    20 
    21 
    22 class CButton: public CObjectX
    23 {
    24 public:
    25     void FireClick()
    26     {
    27         CClickEventArgs args;
    28         OnClicked(this, args);
    29     }
    30 
    31     Common::CEvent<int, CObjectX*, CClickEventArgs&> OnClicked;
    32 };
    33 
    34 
    35 class CMyClass 
    36 {
    37 public:
    38     int OnBtuttonClicked(CObjectX* pButton, CClickEventArgs& args)
    39     {
    40         cout << "CMyClass: Receive button clicked event" << endl;
    41         return 1;
    42     }
    43 };
    44 
    45 int OnBtuttonClicked_C_fun(CObjectX* pButton, CClickEventArgs& args)
    46 {
    47     cout << "C Style Function: Receive button clicked event" << endl;
    48     return 1;
    49 }
    50 
    51 
    52 class CMyFunObj
    53 {
    54 public:
    55     int operator()(CObjectX* pButton, CClickEventArgs& args)
    56     {
    57         cout << "Functor: Receive button clicked event" << endl;
    58         return 1;
    59     }
    60 };
    61 
    62 int _tmain(int argc, _TCHAR* argv[])
    63 {
    64     using namespace std::placeholders;
    65 
    66     CButton btn;
    67 
    68     CMyClass obj;
    69     Common::cookie_type c1 = btn.OnClicked.AddHandler(&obj, &CMyClass::OnBtuttonClicked);
    70 
    71     Common::cookie_type c2 = btn.OnClicked.AddHandler(OnBtuttonClicked_C_fun);
    72 
    73     CMyFunObj functor;
    74     Common::cookie_type c3 = btn.OnClicked.AddHandler(functor);
    75 
    76     btn.FireClick();
    77 
    78 
    79     btn.OnClicked.RemoveHandler(c2);
    80 
    81     std::cout << endl;
    82 
    83 
    84     btn.FireClick();
    85 
    86     system("pause");
    87 
    88     return 0;
    89 }
    90 

    以下是测试结果:


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

    另外对于事件函数返回值为void的情况,会编译出错,我们需要偏特化一下:
      1     template< typename T1, typename T2>
      2     class CEvent<void, T1, T2>
      3     {
      4     public:
      5         typedef void return_type;
      6         typedef T1 first_type;
      7         typedef T2 second_type;
      8 
      9         typedef std::function<return_type (first_type, second_type)> handler_type;
     10 
     11         ~CEvent()
     12         {
     13             Clear();
     14         }
     15 
     16         return_type operator()(first_type p1, second_type p2)
     17         {
     18             size_t size = _handlers.size();
     19             for(size_t i=0; i<size; ++i)
     20             {
     21                 _handlers[i]->operator()(p1, p2);
     22             }
     23         }
     24 
     25         cookie_type AddHandler(std::function<return_type (first_type, second_type)> h)
     26         {
     27             CEventHandler*= new(nothrow)  CEventHandler(h);
     28             if(p != nullptr) _handlers.push_back(p);
     29             return (cookie_type)p;
     30         }
     31 
     32         template<typename class_type, typename class_fun>
     33         cookie_type AddHandler(class_type* pThis, class_fun f)
     34         {
     35             CEventHandler* p = new(nothrow) CEventHandler(pThis, f);
     36             if(p != nullptr) _handlers.push_back(p);
     37             return (cookie_type)p;
     38         }
     39 
     40         void RemoveHandler(cookie_type cookie)
     41         {
     42             CEventHandler* p = (CEventHandler*)cookie;
     43 
     44             auto itr = std::find(_handlers.begin(), _handlers.end(), p);
     45             if(itr != _handlers.end())
     46             {
     47                 _handlers.erase(itr);
     48                 delete p;
     49             }
     50             else
     51             {
     52                 assert(false);
     53             }
     54         }
     55 
     56         void Clear()
     57         {
     58             if(!_handlers.empty())
     59             {
     60                 int n = _handlers.size();
     61                 std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler* p)
     62                 { 
     63                     assert(p != nullptr);
     64                     delete p;
     65                 });
     66                 _handlers.clear();        
     67             }
     68         }
     69 
     70     private:
     71         class CEventHandler 
     72         {
     73         public:
     74             CEventHandler(handler_type h)
     75             {
     76                 _handler = h;
     77                 assert(_handler != nullptr);
     78             }
     79 
     80             template<typename class_type, typename class_fun>
     81             CEventHandler(class_type* pThis, class_fun object_function)
     82             {
     83                 using namespace std::placeholders;
     84                 _handler = std::bind(object_function, pThis, _1, _2);
     85                 assert(_handler != nullptr);
     86             }
     87 
     88             return_type operator()(first_type p1, second_type p2)
     89             {
     90                 assert(_handler != nullptr);
     91                 if(_handler != nullptr) _handler(p1, p2);
     92             }
     93 
     94             handler_type _handler;
     95         };
     96 
     97 
     98     private:
     99         std::vector<CEventHandler*> _handlers;
    100     };

    最后谈一下在写这个代码中遇到的问题:
    (1)不知道你能不能发现下面代码的问题, 我在写代码时就栽在这里了:
      1    vector<int*>  v;
     2    int* p1 = new int(1);
     3    v.push_back(p1);
     4    int* p2 = new int(2);
     5    v.push_back(p2);
     6 
     7    //尝试删除所有值为p1的项
     8    auto itr = remove(v.begin(), v.end(), p1);
     9    for_each(itr, v.end(), [](int* p){delete p;});
    10    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);
    太不方便了, 不知道大家有没有好的方法。

    注:上面的代码还没有经过真正商业使用,如果有问题欢迎指出。
     
    在上文 在C++中实现事件(委托) 中我们实现的C#里委托方式的事件处理, 虽然使用很方便,但是感觉似乎少了一点C#的味道, 下面我们尝试把它改成真正的C#版。

    其实要改成真正的C#版,我们主要要做2件事, 一是吧CEventHandler放到外面,可以让外部直接构造, 二是实现operator +=和operator -=, 下面是我的实现代码:
    #pragma once

    #include <functional>
    #include <algorithm>
    #include <vector>
    #include <assert.h>

    namespace Common
    {
        typedef void* cookie_type;

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

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

            CEventHandler(const CEventHandler& h)
            {
                _handler = h._handler;
                assert(_handler != nullptr);
            }

            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;
        };

        template<typename EventHandler>
        class CEvent
        {
        public:
            typedef EventHandler event_handler_type;
            typedef typename event_handler_type::return_type return_type;
            typedef typename event_handler_type::first_type first_type;
            typedef typename event_handler_type::second_type second_type;
            typedef typename event_handler_type::handler_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(auto p : _handlers)
                {
                    ret = p->operator()(p1, p2);
                }
                return ret;
            }

            cookie_type AddHandler(const event_handler_type& h)
            {
                event_handler_type*p = new(nothrow)  event_handler_type(h);
                if(p != nullptr) _handlers.push_back(p);
                return (cookie_type)p;
            }

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

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

            cookie_type operator += (const event_handler_type& pHandler)
            {
                return AddHandler(pHandler);
            }

            void operator -= (cookie_type cookie)
            {
                RemoveHandler(cookie);
            }

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

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

    //Common

    然后我们就可以这样使用了:
    // EventTest.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"

    #include <iostream>
    #include "event2.h"

    using namespace std;

    class CObjectX 
    {

    };

    class CClickEventArgs: public CObjectX
    {

    };


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

        typedef Common::CEventHandler<int, CObjectX*, CClickEventArgs&> ButtonClickEventHandler;
        Common::CEvent<ButtonClickEventHandler> 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[])
    {
        CButton btn;

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

        Common::cookie_type c2 = btn.OnClicked += CButton::ButtonClickEventHandler(OnBtuttonClicked_C_fun);

        CMyFunObj functor;
        Common::cookie_type c3 = btn.OnClicked += CButton::ButtonClickEventHandler(functor);

        btn.FireClick();

        btn.OnClicked -= c2;

        std::cout << endl;

        btn.FireClick();

        system("pause");

        return 0;
    }

    怎么样,是不是感觉和C#一样了 !


    最后,我们比较一下2种实现方式:
    第一种方法把委托函数类型封装起来了,对外部来说是透明的, 使用起来更简单。
    第二种方式把委托函数的类型暴露了出来, 适用于事件处理函数类型各异,比较强调事件处理函数类型的场合。

    其实对于C++来说,个人觉得还是第一种方式更合理, 不知道大家怎么看?
     
     
    分类: C++
     
  • 相关阅读:
    Intellij IDEA13 创建多模块Maven项目
    oracle锁
    oracle rac负载均衡
    awk命令
    政务外网、政务专网、政务内网和互联网
    图片切换实现选中-未选中效果
    生成带logo 的二维码
    控制input为number时样式
    移动端适配的解决方法?
    input-checkbox选中及非选中样式设置
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2888138.html
Copyright © 2011-2022 走看看