ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C++实现委托机制(三)——lambda表达式封装

2019-12-18 14:01:06  阅读:198  来源: 互联网

标签:封装 函数 CMethodDelegate 委托 C++ func 表达式 lambda


C++实现委托机制(三)——lambda表达式封装
1.引言:              其实原本没打算写这一章的,不过最后想了想,嗯还是把lambda表达式也一并封装进去,让这个委托也适应lambda表达式的注册。不过在之前还是需要先了解lambda表达式。 2.lambda表达式:              如果大家还有对lambda表达式不了解的可以先去了解lambda表达式的基本语法和用法。这里我们只讲跟lambda表达式封装相关的知识。我们先来看看使用lambda表达式的好处吧:1.lambda表达式可以使得在使用的地方定义相关函数,这样给阅读代码带来一定的方便。2.lambda表达式用法相当函数对象(其实可以说就是一个函数对象不过有点区别)这样就可以和很多函数适配符相结合使用。3.lambda表达式可以获取其作用域的任何动态变量。         以下是我经过试验后得出个人的观点:         c++对于lambda表达式的处理应该是当作一个匿名仿函数对象处理。并且其重载operator()函数被申明为const,也就说在定义lambda表达式的时候其封包进去的变量并不是作为这这个类的成员变量,可能是作为一个作为一个绑定值传入的。并且不能用这个lambda表达式推演的类型定义新的对象,否则所有封包进去的变量都会丢失。换句话意思就是一个lambda表达式作为一个函数对象且该类型对象唯一。         每次显示定义一个lambda表达式都是一个新的类型,即便是这两个表达式完全一样!!如果对于某一个特定的lambda表达式你想多次当作参数传入并且类型唯一,那么你最好选择使用一个变量存下这个lambda表达式(变量类型可以使用auto自动推断)。 3.lambda表达式封装委托:           嗯其实大家看了上面的lambda表达式后其实会发现对于lambda表达式可以看作是注册这对象的operator()这个成员函数,只不过这个成员函数是const。所以说我们需要在我们的成员函数特化新加一个const版本: //成员函数委托特化const版 template<typename T, typename _Ret, typename ..._Args> class CMethodDelegate<T, _Ret(T:: *)(_Args...)const> : public IDelegate<_Ret, _Args...> { public: typedef _Ret(T::*Method)(_Args...)const; CMethodDelegate(T * _object,const Method _method) : mObject(_object), mMethod(_method) { } virtual bool isType(const std::type_info& _type) { return typeid(CMethodDelegate<T, _Ret(T:: *)(_Args...)const>) == _type; } virtual _Ret invoke(_Args...params) { return (mObject->*mMethod)(params...); } virtual bool compare(IDelegate<_Ret, _Args...> *_delegate) const { if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<T, _Ret(T:: *)(_Args...)const>))) return false; CMethodDelegate<T, _Ret(T:: *)(_Args...)const>* cast = static_cast<CMethodDelegate<T, _Ret(T:: *)(_Args...)const>*>(_delegate); return cast->mObject == mObject && cast->mMethod == mMethod; } CMethodDelegate(){} virtual ~CMethodDelegate(){} private: T * mObject; Method mMethod; };           嗯,类型封装好了但是需要给出生成委托的接口,我们还是希望传入lambda表达式(也可以是lambda表达式类型的对象)后能够自动推断其参数类型、返回值类型。但是这个时候我们需要考虑一个问题就是,普通函数的生成接口也是一个参数,lambda表达式的生成接口也是一个参数。然而这两个生成委托的方法却不一样,这就是需要我们考虑如何重载的问题,最后我给出一个写好的代码: //生成所有普通函数、成员静态函数委托的接口 template< typename Ret, typename ...Params> CStaticDelegate<Ret(*)(Params...)>* newDelegate(Ret(*func)(Params...)) { return new CStaticDelegate<Ret(*)(Params...)>(func); } //生成所有成员非静态函数委托的接口 template< typename T,typename F> CMethodDelegate<T,F>* newDelegate(T * _object, F func) { return new CMethodDelegate<T, F>(_object, func); } //生成所有函数对象委托的接口 template< typename T> CMethodDelegate<T, decltype(&T::operator())>* newDelegate(T& func) { return new CMethodDelegate<T, decltype(&T::operator())>(&func, &T::operator()); }           这样的话我们就可以使用newDelegate(lambda表达式)来使用了。 4.多播委托委托的使用方法:           如何创建多播委托,多播委托的就是可以注册多个对象,那么我们先定义一个多播委托: CMultiDelegate<void,int> e1; //void(int) CMultiDelegate<void> e2; //void() CMultiDelegate<int,int,double> e3; //int(int,double) 可见定义的类型就是决定可以注册的函数类型。      使用 += 和 -= 可以注册和注销一个委托。      对于成员函数第一个参数类型为对象指针,如果需要创建一个临时变量指针请不要使用new T(),请使用 &T().     还有就是对于lambda表达式的注册和注销有需要值得注意的地方就是,如果你后期可能会对lambda表达式进行注销的话请不要直接传入lambda表达式,请先用一个对象存下该函数对象,然后注册和注销请传入该对象。 int _tmain(int argc, _TCHAR* argv[]) { CMultiDelegate<void,int> e; //void(int) e += newDelegate([](int a){ printf("这是lambda表达式\n"); }); //请尽量不要使用这种方式。 e -= newDelegate([](int a){ printf("这是lambda表达式\n"); }); //这样是无法注销的。因为上面那个lambda表达式和下面这个类型不一样!! //请使用下面这种方式: auto func = newDelegate([](int a){ printf("这是lambda表达式\n"); });; e += func; e -= func; return 0; }         基本上委托就封装到这里了。

 

标签:封装,函数,CMethodDelegate,委托,C++,func,表达式,lambda
来源: https://www.cnblogs.com/leijiangtao/p/12059510.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有