C++实现的委托机制(一)

来源:互联网 发布:淘宝助理5.5官方 编辑:程序博客网 时间:2024/06/04 14:56

C++实现的委托机制(一)

作者:gouki04(啊古)  撰写日期:2011-09-27

博客链接:http://blog.csdn.net/gouki04

 1.引言

    下面的委托实现使用的MyGUI里面的委托实现,MyGUI是一款强大的GUI库,想理解更多的MyGUI信息,猛击这里http://mygui.info/ 。最终的代码可以在这里下载:http://download.csdn.net/detail/gouki04/3641328 我们的目标是要实现一个跟.NET几乎完全一样的委托,使用简单,支持多播,可以添加删除委托。同时支持C++的普通函数、模板函数、类成员函数,类的静态成员函数,并且支持多态。使用方式如下:

// 普通函数void normalFunc(){ cout << "func1" << endl; }class Base{public:// 类成员函数void classFunc(){ cout << "Base func1" << endl; }};int main(){Base b;CMultiDelegate myDelegate;myDelegate += newDelegate(normalFunc);myDelegate += newDelegate(&b, &Base::classFunc);myDelegate(); // 此时会调用normalFunc和classFuncmyDelegate -= newDelegate(&b, &Base::classFunc);myDelegate(); // 此时会调用normalFuncreturn 0;}

2.实现无参函数委托

    要实现委托,首先要解决的是封装C++中的函数指针。因为在C++中,普通函数指针和类成员函数指针是完全不一样的。如下例子:

class CMyClass{public:    void func(int);};

    定义一个指向CMyClass类型,参数列表为(int),返回值为void的函数指针typedef void (CMyClass::*ClassMethod) (int); // 注意定义时使用了特殊的运算符::*。那么此函数指针只能指向CMyClass类型的成员函数,不能指向其他类或者普通函数。类成员函数指针不能直接调用,要通过一个类实例来调用,如下:
CMyClass *object = new CMyClass;ClassMethod method = CMyClass::func;(object->*method)(5); // 注意调用时使用了特殊运算符->*

    那么如何封装呢?我们先来定义下接口吧。(为了简单起见,下面的实现都是以无参函数为例,后续会讲到如何支持任意参数)

class IDelegate{public:    virtual ~IDelegate() { }    virtual bool isType(const std::type_info& _type) = 0;    virtual void invoke() = 0;    virtual bool compare(IDelegate *_delegate) const = 0;};

    IDelegate类的接口很少,也很简单,必要接口只有一个,就是invoke,用于触发函数,但为了可以方便管理,使用了isType和compare函数来进行相等判断。下面是封装的普通函数指针:

class CStaticDelegate : public IDelegate{public:    typedef void (*Func)();    CStaticDelegate(Func _func) : mFunc(_func) { }    virtual bool isType(const std::type_info& _type) { return typeid(CStaticDelegate) == _type; }    virtual void invoke() { mFunc(); }    virtual bool compare(IDelegate *_delegate) const    {        if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate)) ) return false;        CStaticDelegate * cast = static_cast<CStaticDelegate*>(_delegate);        return cast->mFunc == mFunc;    }private:    Func mFunc;};

    可以看到,CStaticDelegate只是简单地封装了普通函数指针,代码也非常简单,(类的某些成员函数,如isType和compare使用了RTTI,对C++的动态类型判断不熟的可以猛击这里http://blog.csdn.net/gouki04/article/details/6796173)好了,注意了,下面开始封装类成员函数指针:

template<class T>class CMethodDelegate : public IDelegate{public:    typedef void (T::*Method)();    CMethodDelegate(T * _object, Method _method) : mObject(_object), mMethod(_method) { }    virtual bool isType( const std::type_info& _type) { return typeid(CMethodDelegate) == _type; }    virtual void invoke()    {        (mObject->*mMethod)();    }    virtual bool compare(IDelegate *_delegate) const    {        if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate)) ) return false;        CMethodDelegate* cast = static_cast<CMethodDelegate* >(_delegate);        return cast->mObject == mObject && cast->mMethod == mMethod;    }private:    T * mObject;    Method mMethod;};

    首先解释一下:因为类成员函数指针与类的类型有关,不同类的成员函数指针是不一样的。要解决类型不同,很简单,使用模板就行。代码跟CStaticDelegate基本一样,下面稍微解释一下:CMethodDelegate类主要封装了一个类实例指针以及类成员函数的指针,这样在invoke时就不要额外的通过一个类实例了。要注意一点,compare函数的实现中,相等判定是类实例以及类函数指针都一样。也就是说就算是指针同一个成员函数,但实例不同,委托就不同。为了方便使用,定义函数newDelegate来创建委托使用的函数:

inline IDelegate* newDelegate( void (*_func)() ){    return new CStaticDelegate(_func);}template<class T>inline IDelegate* newDelegate( T * _object, void (T::*_method)() ){    return new CMethodDelegate<T>(_object, _method);}

    至此,对C++函数指针的封装就完成了,不难吧。下面就是委托的实现了:

class CMultiDelegate{public:    typedef std::list<IDelegate*> ListDelegate;    typedef ListDelegate::iterator ListDelegateIterator;    typedef ListDelegate::const_iterator ConstListDelegateIterator;    CMultiDelegate () { }    ~CMultiDelegate () { clear(); }    bool empty() const    {        for (ConstListDelegateIterator iter = mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)        {            if (*iter) return false;        }        return true;    }    void clear()    {        for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)        {            if (*iter)            {                delete (*iter);                (*iter) = 0;            }        }    }    CMultiDelegate& operator+=(IDelegate* _delegate)    {        for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)        {            if ((*iter) && (*iter)->compare(_delegate))            {                delete _delegate;                return *this;            }        }        mListDelegates.push_back(_delegate);        return *this;    }    CMultiDelegate& operator-=(IDelegate* _delegate)    {        for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)        {            if ((*iter) && (*iter)->compare(_delegate))            {                if ((*iter) != _delegate) delete (*iter);                (*iter) = 0;                break;            }        }        delete _delegate;        return *this;    }    void operator()( )    {        ListDelegateIterator iter = mListDelegates.begin();        while (iter != mListDelegates.end())        {            if (0 == (*iter))            {                iter = mListDelegates.erase(iter);            }            else            {                (*iter)->invoke();                ++iter;            }        }    }private:    CMultiDelegate (const CMultiDelegate& _event);    CMultiDelegate& operator=(const CMultiDelegate& _event);private:    ListDelegate mListDelegates;};

仔细理解下CMultiDelegate类的实现,代码都不深奥。

比较重要的是3个函数 :+=,-=,()运算符的重载函数

+= 用于添加一个委托函数

-= 用于去掉一个委托函数

()用于触发委托函数

差不多就是普通的STL容器使用了。

这里要重点说明的一点是,大家仔细看 += 函数的实现中

if ((*iter) && (*iter)->compare(_delegate)){delete _delegate; // 如果该委托函数已经被添加了,则delete掉外部的_delegatereturn *this;}

    为什么要delete掉外部的指针呢?因为C++的内存泄露一直是个麻烦事,所以MyUGI的委托里,所有的委托函数统一由Delegate本身管理,外部不要自己new或delete委托函数,也不要保存一个委托函数,Delegate本身会管理好的。建议像如下使用:

CMultiDelegate myDelegate;myDelegate += newDelegate(normalFunc);myDelegate -= newDelegate(normalFunc);//而不建议像如下使用:CMultiDelegate myDelegate;IDelegate* delegateFunc = newDelegate(normalFunc);myDelegate += delegateFunc;myDelegate -= delegateFunc;

    上面2种方法都没错,都不会造成内存泄露。你可能会觉得第2种方法减少new的次数,比第一种方法更好。其实不然,因为第2种方法有个很大的隐患,myDelegate -= delegateFunc;// 在这一步,delegateFunc所指向的空间已经被释放掉了(在-=函数里面),所以如果你后面又想将delegateFunc添加到myDelegate里面时,你就不能再这样用了。myDelegate += delegateFunc;// 错误,因为delegateFunc的空间已经被释放了。你得重新new一个:

delegateFunc = newDelegate(normalFunc);

myDelegate += delegateFunc;

       相信你不会愿意这样做的,因为这种方法很容易造成内存泄露或者崩溃,现在你应该可以明白 -= 函数是怎么释放委托函数内存了吧。按上面的方法,你已经可以使用无参数的函数委托了。下一篇文章将会介绍如何实现任意参数的函数委托。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 自己没钱了还想贷款买手机怎么办 4g手机锁屏密码忘了怎么办 oppo手机锁屏密码忘了怎么办 三星手机锁屏密码忘了怎么办 网上买的手机想退了怎么办 沙棘排湿排毒后皮肤痒怎么办 做了沙棘排毒两次上上痒怎么办? 微信支付打款认证超时怎么办 微信上买东西钱付款了货没发怎么办 掌上道聚城王者轮回分解错了怎么办 鞋子脱胶了怎么办别傻傻用胶水粘 斗鱼主播积分太低无法收礼物怎么办 信誉积分没有了什么也玩不了怎么办 忘记自己电信卡号的密码怎么办 淘宝卖出去东西的钱被冻结了怎么办 手机店铺搞抽奖活动上当了怎么办 网易云音乐上传作品编辑失败怎么办 手机连了wifi后自动扣费怎么办 店铺被低价拍了一部分商品该怎么办 淘宝买家退货快递单号填错了怎么办 卖家同意退款了买家不退货怎么办 淘宝代购衣服有破损不给退怎么办 网上购飞机票身份证号写错了怎么办 交易猫买家把我号清了怎么办 光大普卡额度4万封顶了怎么办 天天特价通过后价格设置错误怎么办 苏宁易购不小心点了免密支付怎么办 别人知道自己京东的账号密码怎么办 oppo手机刷机要锁屏密码怎么办 在苏宁易购退款成功又收到货怎么办 今日头条转发出现未安装微信怎么办 打开手机后自动弹出广告怎么办去除 朋友圈发广告被腾讯屏蔽了怎么办 新商盟手机订烟登录密码忘了怎么办 手机版战神斯巴达幽灵经常崩怎么办 代购买的东西被海关扣了怎么办 托朋友代购给了钱联系不上人怎么办 签了合同被加盟商骗了怎么办 百度云盘下载时本地空间不足怎么办 百度云盘隐私空间没密码忘了怎么办 华为云空间的帐号密码忘记了怎么办