zoukankan      html  css  js  c++  java
  • 在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);
    太不方便了, 不知道大家有没有好的方法。

    注:上面的代码还没有经过真正商业使用,如果有问题欢迎指出。
  • 相关阅读:
    7月15日考试 题解(链表+状压DP+思维题)
    暑假集训日记
    C# .NET 使用 NPOI 生成 .xlsx 格式 Excel
    JavaSE 基础 第42节 局部内部类
    JavaSE 基础 第41节 匿名内部类
    JavaSE 基础 第40节 内部类概述
    JavaSE 基础 第39节 接口的应用
    JavaSE 基础 第38节 接口的实现
    JavaSE 基础 第37节 接口概述
    JavaSE 基础 第36节 抽象类概述与使用
  • 原文地址:https://www.cnblogs.com/weiym/p/2886965.html
Copyright © 2011-2022 走看看