C++中Lambda捕获类对象引用后,超出作用域为何仍正常输出?
为什么引用已销毁对象的Lambda调用没有崩溃?
先看你写的C++代码:
class sample1 { public: std::function<void()> p1; void configure(std::function<void()> TCallbackFn) { p1 = TCallbackFn; } }; class test { public: int a; test(int a) : a(a) { } ~test() { } void print() const { cout<<"Hello "<<a; } }; int main() { sample1 obj; { test t(4); obj.configure([&]() { t.print(); }); } obj.p1(); }
这不是编译器优化导致的,而是未定义行为的一种巧合表现。
当局部作用域结束后,test对象t的内存会被标记为可回收,但操作系统或程序运行时并不会立刻清空或覆盖这块内存的内容。你的Lambda捕获的是t的引用,调用obj.p1()时,刚好这块内存还没被其他代码占用改写,所以访问a时读出了原来的4,就输出了"Hello 4"。
未定义行为的结果是完全不可预测的:如果在t销毁后、调用obj.p1()前,有其他代码分配并使用了这块内存,那你可能会看到垃圾值,甚至程序直接崩溃。编译器没有特意保留已销毁对象的内存内容,这只是刚好没被覆盖而已。
用伪代码模拟这个内存过程:
// 模拟内存状态变化 main() { 为obj分配内存; { 在栈上分配内存给t,地址假设为0x100,其中a的值设为4; 将捕获0x100地址引用的Lambda赋值给obj.p1; // t离开作用域:标记0x100地址的内存为可用,但内存里的4还没被改写 } // 此时0x100地址的内存未被其他操作占用 调用obj.p1() → 访问0x100地址里的a,读出4,输出Hello 4; }
内容的提问来源于stack exchange,提问作者Krishna Padia
相关产品推荐
相关产品推荐

