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


    C++中实现回调机制的几种方式一文中,我们提到了实现回调的三种方式(C风格的回调函数, Sink方式和Delegate方式)。在面向对象开发中,delegate的方式是最灵活和方便的,因此很早就有人用复杂的模板去模拟(有兴趣的话可以看这里这里),总之是实现起来很复杂。但是现在借助C++11的functionbind, 我们可以很方便的去实现。下面是我自己的一种实现方式:
      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);
    太不方便了, 不知道大家有没有好的方法。

    注:上面的代码还没有经过真正商业使用,如果有问题欢迎指出。
  • 相关阅读:
    【BZOJ 3569】DZY Loves Chinese II
    【POJ3155】生活的艰辛Hard Life
    【SGU194】Reactor Cooling
    Elasticsearch 6.1.2 搭建及使用教程一
    Centos7安装Mysql-最方便、最快捷
    Realm_King 之 .NET 打包详细教程(A)
    C#程序员经常用到的10个实用代码片段
    使用VS2010再装VS2013不用再烦恼不兼容
    表变量和临时表的使用
    golang连接ftp服务器
  • 原文地址:https://www.cnblogs.com/weiym/p/2886965.html
Copyright © 2020-2023  润新知