C++对象模型之lambda表达式
lambda表达式的求值-对象构造
本来想写“定义”,即“definition”,像函数定义一样,函数具体实现的代码实体即为实现,但是就像lambda既然被称为表达式,它确实有表达式那样“求值”的动作,而不仅仅像函数那样静态地编译。所以应该写“求值”更确切些,即“evaluation”。如果lambda定义的仅仅是一个函数,也就是返回一个函数指针,那么这里就应该叫做“定义”,但是lambda实际上定义了一个“函数对象”,即“function object”。
首先像下面这样定义一个简单的lambda表达式:

设好断点,在调试状态下查看相应的汇编代码:

这是设置为不显示符号名称后更原汁原味的汇编代码:

可以看到,编译器将a,b的地址压栈,这里都是地址,虽然对于变量a我们是by value而不是by reference。接下来,把lbd的地址装入ecx,然后call,很明显的thiscall,那么lbd自然就是一个object,那么此时此刻被调用的当然就是constructor。
跟踪到constructor代码处继续看,跳过函数入口的stack frame、寄存器保存等操作,直接定位到目标代码:

不显示符号名称,对比着看:

首先,把ecx的值存到堆栈上的this指针,然后通过堆栈上a的地址取到a的值,把a的值存储到this所指对象(即上面的lbd)0偏移处,占4字节(一个int),最后把堆栈上b的地址存储到this所指对象4字节偏移处,占4字节(32位平台指针大小)。一个值、一个地址,在这里我们就可以体会到“by value”和“by reference”的实现原理了。a的值和b的地址在这里就被存储到了lambda对象体内部,对程序员不可见,在对象的生命周期内都不会改变。
所以我们可以推导出,lbd对象的class定义为:
class Clambda
{
public:
Clambda(int &a, int &b)
{
this->a = a;
this->pb = &b;
}
protected:
int a;
int *pb;
};
或者写成这样:
class Clambda
{
public:
Clambda(int &a, int &b) : a(a), b(b)
{
}
protected:
int a;
int &b;
};
lambda表达式的定义,即表达式的求值,也就是等价类对象的实例化。
lambda表达式的Function Call Operator
在原有代码基础之上,加一句调用代码:

在调试汇编窗口查看:

不显示符号名称:

不出所料,在这里我们又看到了thiscall。继续跟踪到函数内部:

不显示符号名称:

首先从栈上取到x的值,然后通过this指针取到对象体0偏移处4个字节即变量a的值(“by value”,这个值是lambda对象定义时刻的值),然后相乘,之后通过this指针取到对象体4字节偏移处存储的指向变量b的指针,进一步得到b的值(“by reference”,当前时刻的值),再相乘得到结果。
现在,我们可以完善lbd的class定义:
class Clambda
{
public:
Clambda(int &a, int &b)
{
this->a = a;
this->pb = &b;
}
int operator()(int x)
{
return x * a * *pb;
}
protected:
int a;
int *pb;
};
或者:
class Clambda
{
public:
Clambda(int &a, int &b) : a(a), b(b)
{
}
int operator()(int x)
{
return x * a * b;
}
protected:
int a;
int &b;
};
用class实现等价lambda
把原来代码中的lambda表达式用我们上面定义的Clambda类替换,汇编代码如下:
对象构造和function call:

不显示符号名称:

构造函数:

不显示符号名称:

function call函数体:

不显示符号名称:

结束语
lambda的出现大大方便了function object的创建,编译器帮我们做了多余的工作。然而lambda带来方便的同时,也有很多值得注意的地方,例如笔者在做Win8 Metro开发的时候,就曾不止一次内存访问异常,究其原因就是因为异步lambda按引用捕获的外层函数局部变量已销毁。了解了lambda的底层原理,应该可以帮我们更好的避免此类错误,用好lambda。
喜爱C++,喜爱lambda。
C++对象模型之lambda表达式的更多相关文章
- 十二、C# 委托与Lambda表达式(匿名方法的另一种写法)
委托与Lambda表达式 1.委托概述 2.匿名方法 3.语句Lambda 4.表达式Lambda 5.表达式树 一.委托概述 相当于C++当中的方法指针,在C#中使用delegate 委托来 ...
- 你知道C#中的Lambda表达式的演化过程吗?
那得从很久很久以前说起了,记得那个时候... 懵懂的记得从前有个叫委托的东西是那么的高深难懂. 委托的使用 例一: 什么是委托? 个人理解:用来传递方法的类型.(用来传递数字的类型有int.float ...
- Linq表达式、Lambda表达式你更喜欢哪个?
什么是Linq表达式?什么是Lambda表达式? 如图: 由此可见Linq表达式和Lambda表达式并没有什么可比性. 那与Lambda表达式相关的整条语句称作什么呢?在微软并没有给出官方的命名,在& ...
- 背后的故事之 - 快乐的Lambda表达式(一)
快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...
- Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)
作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...
- java8中lambda表达式的应用,以及一些泛型相关
语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...
- 背后的故事之 - 快乐的Lambda表达式(二)
快乐的Lambda表达式 上一篇 背后的故事之 - 快乐的Lambda表达式(一)我们由浅入深的分析了一下Lambda表达式.知道了它和委托以及普通方法的区别,并且通过测试对比他们之间的性能,然后我们 ...
- CRL快速开发框架系列教程二(基于Lambda表达式查询)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- Lambda 表达式递归用法实例
注意: 使用Lambda表达式会增加额外开销,但却有时候又蛮方便的. Windows下查找子孙窗口实例: HWND FindDescendantWindows(HWND hWndParent, LPC ...
随机推荐
- VB6文件操作自定义函数合集之一
'--与文件及文件夹操作相关的函数 '--必须引用FSO的ACTIVE OBJECT Dim strList As String '--列表串,返回文件列表 '================ '-- ...
- phpstudy升级mysql数据库
因为MySQL支持全文索引的只有5.6以上,而我下的phpstudy只有5.5的版本,在导入数据库的时候因为该数据库的表内有使用全文索引,因此必须升级phpstudy的mysql版本,这里就把自己当升 ...
- Java可变参数以及一个简单应用
可变参数: Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理. 注意:可变参数必须位于最后一项. 原因:当可变参数个数多余一个时,必将有一个不 ...
- HTML之头部标签
<header> head 元素元素包含了所有的头部标签元素 可以添加在头部区域的元素标签为: title,style,meta,link,script,noscript,base < ...
- 【NOIP2016提高组】 Day2 T2 蚯蚓
题目传送门:https://www.luogu.org/problemnew/show/P2827 自测时被题面所误导...,题面中说逢t的倍数才输出答案,以为有什么玄妙的方法直接将m次操作变成了m/ ...
- Markdown不常见功能
推荐几个Markdown不常见功能 1.表情符号 emoji表情使用:EMOJICODE:的格式,详细列表可见 https://www.webpagefx.com/tools/emoji-cheat- ...
- 关于docker使用的几个小问题(一)
由于刚接触docker踩了几个坑,希望本文对网瘾少年有所帮助. Docker分CE版(社区版)和EE版(商用版),具体安装流程参考文档介绍,在此不再赘述.下面分Windows和Linux分别踩坑: 一 ...
- 关于Python输出时间戳的问题
在我们的程序中,有时候想要知道程序的执行时间或者准确的停止时间,这时候就需要我们自己添加一个时间戳,以便我们做出判断和相应的处理. 下面是我亲测并收集的资料,菜鸟一枚,不全之处大神可给予补充和指正. ...
- Setup and Configure the vsftpd server in CentOS 7 operation system
############################################################################## 1. close the firewall ...
- 剑指offer--面试题3
一 题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.完成一个函数,输入这样的数组和一整数,判断这个数组是否包含这个整数. 二 分析 如果这个二维数组是 ...