// lambda_test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <functional>
// 利用rtii观察堆栈生命周期
class StackLifeTimeWatching
{
public:
StackLifeTimeWatching()
{
std::cout << __FUNCTION__ << std::endl;
}
~StackLifeTimeWatching()
{
std::cout << __FUNCTION__ << std::endl;
}
};
// 经验: 保存lambda表达式的变量被销毁后,该表达式对应的闭包会销毁。应该保证闭包在lambda表达式变量的生命周期之内执行,否则程序执行结果不可预知!
// 1.理解lambda首先要理解函数对象,和闭包
// 2.理解必包的基础上,理解lambda如何实现闭包
// 3.理解闭包以后,需要分析设置不同的capture的情况下分别是如何实现闭包
// 1)闭包意味着一个函数地址 + 一组封装好的参数。
// 2)闭包可以被拷贝,但是每个闭包中的参数可以是不一样的
// 4.理解函数对象和lambda的关系: lambda可以理解成函数,但是当lambda赋值给一个函数对象的时候,编译器应该是把lambda构造成了一个闭包的function
// 1)根据汇编码分析,lambda对象类似于函数指针(但是类型系统和函数指针是完全不同的概念,可以用decltype(lambda)来鉴定),本质和函数对象是不一样的。
// 2)定义一个lambda表达式相当于定义一个函数(观察会变码,lambda表达式是没有构造和析构的)
// 3)把函数指针赋值给一个std::function,和吧lambda赋值给一个std::function的效果是完全不一样的。一个这是指针赋值操作,另一个则是完整的闭包。
// 4)经过代码实际测试,lambda是鉴于函数指针和函数对象之间的一个玩意,它也是一个特殊的类型,这个具体只能看C++标准文档了。
// 5)boost asio异步接口中的functor可能是利用了meta编程技巧,或者他本身每一次发起异步操作都会形成一个独立的闭包,解决了函数对象和socket对象生命周期绑定的关系
// 应为如果是functor实现,宿主对象析构,一定会造成作为成员变量的functor销毁,同时引起lambda闭包混乱(不仅闭包参数乱掉,闭包函数本身也呈现混乱)。
// 由此分析,闭包中的任何一行代码都必须在闭包本身的声明周期内执行。似乎可以理解成,lambda闭包是把lambda函数本身也当作一个特殊的参数来完成闭包封装的。
// 通过会变码观察,在使用不同的lambda变量调用lambda表达式的时候,会在ecx寄存器压入不同的值,然后会读取一块关联的内存。
// 6)vc2015下的lambda永远都是4字节,这应该是编译器实现细节了,按说应该是随着闭包内容的大小变化而变化。我猜测,这四个字节应该指向一个块内存,里边的数据是用来还原“lambda”函数执行栈的闭包
// 5.通俗的理解上述分析: lambda对象(变量)是一块内存,内存里边是lambda表达式本身的副本。当执行lambda表达式对象的时候,实际是执行对象对应的内存中的代码,如果对象被析构了,对应的代码也就是未知代码。
void Test1();
void Test2();
int main()
{
Test2();
return 0;
}
void Test2()
{
int n = 0;
auto lambda = [&]()->void
{
StackLifeTimeWatching stackWatching;
n = 1;
int j = 0;
int j1 = 0;
int j2 = 0;
int j3 = 0;
int j4 = 0;
int j5 = 0;
};
decltype(&lambda) pLambda0 = λ
decltype(&lambda) pLambda = NULL;
int nSize = sizeof(lambda);
{
decltype(lambda) lambda_copy = lambda;
lambda_copy();
}
(*pLambda0)(); // 正常掉用
(*pLambda)(); // 调用后整个闭包混乱
}
void Test1()
{
StackLifeTimeWatching p();
int n = 0;
std::function<void()> func;
std::function<void()>* pFunc = new std::function<void()>;
{
//std::function<void()> func = [&]()->void
//{
// StackLifeTimeWatching stackWatching;
// n = 1;
//};
//func();
auto lambda = [&]()->void
{
StackLifeTimeWatching stackWatching;
n = 1;
};
lambda();
func = lambda;
auto lambda2 = [&]()->void
{
delete pFunc;
pFunc = NULL;
StackLifeTimeWatching stackWatching;
n = 1;
};
//decltype(lambda) lambda_copy = lambda2; 编译错误,应为编译器会把每一个lambda表达式当作一个独立的类型,这是lambda不同于函数指针的地方,函数指针是根据参数来决定类型的
decltype(lambda) lambda_copy = lambda;
*pFunc = lambda2;
}
func();
(*pFunc)();
}