• 在c++中用function与bind实现委托


    委托的核心就是封装类成员函数,使不同类的不同函数能够以一种统一的方式被调用

    这样进行回调的方式要比使用接口通知模型少写很多代码,避免了重复工作

    如果不使用c++11的新特性当然也能自己抡一个类似的轮子(而且还可以随意拓展)

    不过有现成没有理由不用

    function:函数对象,可以认为这个对应于c#中的委托,不过c++11没有提供事件的抽象,下面就要实现它

    bind:创建函数对象的工具

    下面的一个类实现了类似c#事件(在c++里面还是叫委托好了)的模板

    struct __ut_delegate_id
    {
        template<typename T>
        friend class ut_delegate;
    private:
        static int64 s_delageteID;
        static int64  getID()
        {
            return s_delageteID++;
        }
    };
    
    template<typename T>
    class ut_delegate
    {    
    private:
        map<int64,T> m_listeners;
        static int s_Serial;
    public:    
        int64 add(T func)
        {
            int64 id=__ut_delegate_id::getID();
            m_listeners.insert(pair<int64,T>( id,func));
            return id;
        }
        void remove(int64 id)
        {
            m_listeners.erase(id);
        }
        void NotifyAll()
        {
            for_each(m_listeners.begin(),m_listeners.end(),[&](pair<int64,T> func){
                func.second();
            });
        }
        template<typename T1>
        void NotifyAll(T1 t1)
        {
            for_each(m_listeners.begin(),m_listeners.end(),[&](pair<int64,T> func){
                func.second(t1);
            });
        }
        template<typename T1,typename T2>
        void NotifyAll(T1 t1,T2 t2)
        {
            for_each(m_listeners.begin(),m_listeners.end(),[&](pair<int64,T> func){
                func.second(t1,t2);
            });
        }
         
    };

    使用方法:

    void food1(int a)
    {
        printf("food1 %d\n",a);
    }
    struct fooClass
    {
        void fooFunc(int a,int b)
        {
            printf("wocao %d %d",a,b);
        }
    };
    main:
        ut_delegate<function<void(int)>> del;
        del.add(std::bind(food1,placeholders::_1));
        fooClass* wo1=new fooClass();
    
        del.add(std::bind(&fooClass::fooFunc,wo1,placeholders::_1,555));
        del.NotifyAll(123);

    缺点是接口不友好,你仍然需要显式使用function和bind(bind很丑陋,虽然很灵活)

    这里没有使用+= -= ()之类的操作符,操作符重载看起来很酷,其实经常让人看不懂代码

    类里面有一系列的NotifyAll,不过还好未被调用的模板函数不会被编译  所以这个类能正常工作

    当然,还有一个老大难问题:如果你使用不正确,编译器会报出令人崩溃的编译错误

    还有一个坎:function没有提供==操作符,这给remove带来很大麻烦,但是实际上应该是有方法判断函数对象相等的,因为函数对象实际上就是保存了函数指针和可能的this, 

    我采用直接比较内存的方法判断相等,虽然是个hack,但是能工作,完全fix这个问题需要研究代码,或者进一步测试       

    看了function的代码之后发现实际上function没办法提供这个功能,但是我们可以想个曲线救国的办法,绑定的时候返回一个id,外部释放的时候使用这个id来处理 

    但是我们仍然需要考虑生存期问题,假设现在有委托A,类B把自己的成员函数绑定到A上,那么B销毁时需要去调用remove,那么B需要持有A的指针,但是如果此时A已经销毁,所以A在销毁时需要通知B

    也就是说A,B各自在销毁的时候都要去通知对方,这样又增加了很多复杂度,如果能自动管理生存期就好了!

    但是如果A始终能先于B销毁就没有这个问题了

    话说回来,果然还是自己抡的东西好用..

  • 相关阅读:
    Bootstrap 模态对话框只加载一次 remote 数据的解决办法
    通过反射查找泛型的属性值
    基于Bootstrap的超酷jQuery开关按钮插件
    解決BufferedReader读取UTF-8文件中文乱码(转)
    Hibernate学习笔记
    freemarker XMLGregorianCalendar 转日期
    Android中手机号、车牌号正则表达式
    Eclipse中启动tomcat报错java.lang.OutOfMemoryError: PermGen space的解决方法
    WPF 引用DLL纯图像资源包类库中的图片
    “ sgen.exe ”已退出,代码为 1
  • 原文地址:https://www.cnblogs.com/mightofcode/p/2958159.html
Copyright © 2020-2023  润新知