C++实现委托机制(三)——lambda表达式封装
C++实现委托机制(三)——lambda表达式封装.引言: 其实原本没打算写这一章的,不过最后想了想,嗯还是把lambda表达式也一并封装进去,让这个委托也适应lambda表达式的注册。不过在之前还是需要先了解lambda表达式。 .lambda表达式: 如果大家还有对lambda表达式不了解的可以先去了解lambda表达式的基本语法和用法。这里我们只讲跟lambda表达式封装相关的知识。我们先来看看使用lambda表达式的好处吧:.lambda表达式可以使得在使用的地方定义相关函数,这样给阅读代码带来一定的方便。.lambda表达式用法相当函数对象(其实可以说就是一个函数对象不过有点区别)这样就可以和很多函数适配符相结合使用。.lambda表达式可以获取其作用域的任何动态变量。
以下是我经过试验后得出个人的观点:
c++对于lambda表达式的处理应该是当作一个匿名仿函数对象处理。并且其重载operator()函数被申明为const,也就说在定义lambda表达式的时候其封包进去的变量并不是作为这这个类的成员变量,可能是作为一个作为一个绑定值传入的。并且不能用这个lambda表达式推演的类型定义新的对象,否则所有封包进去的变量都会丢失。换句话意思就是一个lambda表达式作为一个函数对象且该类型对象唯一。
每次显示定义一个lambda表达式都是一个新的类型,即便是这两个表达式完全一样!!如果对于某一个特定的lambda表达式你想多次当作参数传入并且类型唯一,那么你最好选择使用一个变量存下这个lambda表达式(变量类型可以使用auto自动推断)。 .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 ( == _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表达式)来使用了。 .多播委托委托的使用方法:
如何创建多播委托,多播委托的就是可以注册多个对象,那么我们先定义一个多播委托: 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 ;
} 基本上委托就封装到这里了。
C++实现委托机制(三)——lambda表达式封装的更多相关文章
- (28)C#委托,匿名函数,lambda表达式,事件
一.委托 委托是一种用于封装命名和匿名方法的引用类型. 把方法当参数,传给另一个方法(这么说好理解,但实际上方法不能当参数,传入的是委托类型),委托是一种引用类型,委托里包含很多方法的引用 创建的方法 ...
- C#多线程+委托+匿名方法+Lambda表达式
线程 下面是百度写的: 定义英文:Thread每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程.进程也可能是整个程序或者是部分程序的动态执行.线程是一组指令的集合,或者是程序的特殊段,它 ...
- 委托-异步调用-泛型委托-匿名方法-Lambda表达式-事件【转】
1. 委托 From: http://www.cnblogs.com/daxnet/archive/2008/11/08/1687014.html 类是对象的抽象,而委托则可以看成是函数的抽象.一个委 ...
- lambda表达式封装对数据库的查询
前言: 1.为什么要封装lambda表达式数据库查询,原因有一下几点: 1.1.在以往的开发中进行数据库表查询时,其实所需要的字段就是其中几个,但是在开发中,开发者往往习惯select * 进行查询, ...
- 三 lambda表达式有什么用
(转载: https://mp.weixin.qq.com/s/-PHOc6p-qKJBktle28AUgA) 一: 直接把代码块赋值给变量 我们知道,对于一个Java变量,我们可以赋给其一个“值”. ...
- 委托初级篇——lambda表达式的推导
public delegate void ConsoleWriteStr(string name,DateTime now); public delegate int DelegateAdd(int ...
- 委托、回调 Lambda表达式书写方式
- C#从委托、lambda表达式到linq总结
前言 本文总结学习C#必须知道的基础知识,委托.监视者模式.常用lambda表达式.linq查询,自定义扩展方法,他们之间有什么关系呢?匿名委托是如何演变成lambda表达式,lambda再如何导出l ...
- c#委托中的匿名方法和lambda表达式
一.一般委托方式 Func<int, int, int> AddMethodHander; public unName() { AddMethodHander += AddMethod; ...
随机推荐
- 嵌入式Linux框架的理解
从事嵌入式linux工作也几年了,如果算上大学期间的自学,那么也算是个工程师了.期间写过底层bootloader.内核的驱动和上层应用程序.对于芯片内部的模块也在大学时候用fpga的verilog玩过 ...
- C++(四十八) — string容器的基本操作
参考博客:https://blog.csdn.net/qq_37941471/article/details/82107077 https://www.cnblogs.com/danielStudy/ ...
- jquery 表单元素选择器
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content ...
- ThreadLocal源码原理以及防止内存泄露
ThreadLocal的原理图: 在线程任务Runnable中,使用一个及其以上ThreadLocal对象保存多个线程的一个及其以上私有值,即一个ThreadLocal对象可以保存多个线程一个私有值. ...
- 烦人的警告 Deprecated: convertStrings was not specified when starting the JVM
python 调用java代码: Deprecated: convertStrings was not specified when starting the JVM. The default beh ...
- (nohup+开启fitnesse的命令+&)让fitnesse在linux可脱离终端在后台运行
1.脱离终端后台运行fitnesse 用终端连接linux时,开启fitnesse命令后,界面是这样的. 如果此时终端关闭或是不小心按了ctrl+c,fitnesse就被关闭,页面就无法访问了 为了解 ...
- 实训作业6 (数据I/O)
1. 文件输出流的应用. 定义如下字符串: String str = “12345abcdef@#%&*软件工程”; 编写程序将该字符串写入文件”data.txt”. import java. ...
- Switch ……case语句
Switch(变量){ case 1: 如果变量和1的值相同,执行该处代码 break; case 2: 如果变量和2的值相同,执行该处代码 break; case 3: 如果变量和3的值相同,执行该 ...
- [CSP-S 2019]格雷码
[CSP-S 2019]格雷码 题目大意: 格雷码(Gray Code)是一种特殊的 \(n\) 位二进制串排列法,它要求相邻的两个二进制串间恰好有一位不同,特别地,第一个串与最后一个串也算作相邻. ...
- Python题库系列分享一(17道)
1.1 到Python官方网站下载并安装Python解释器环境.1.2 到Anaconda官方网站下载并安装最新的Anaconda3开发环境.1.3 Python程序的__name__的作用是什 ...