Lambda捕获和内存管理

Lambda Capture and Memory Management

本文关键字:内存 管理 Lambda      更新时间:2023-10-16

当我在C++11 lambda中通过引用捕获对象,让该对象超出范围,然后执行lambda时,它仍然可以访问该对象。当我执行以下代码时,lambda调用仍然可以访问对象,尽管已经调用了析构函数!有人能解释一下为什么这有效,为什么我没有得到运行时错误吗?

#include <iostream>
class MyClass {
public:
    int health = 5;
    MyClass() {std::cout << "MyClass created!n";}
    ~MyClass() {std::cout << "MyClass destroyed!n";}
};
int main(int argc, const char * argv[])
{
    std::function<bool (int)> checkHealth;
    if(true) {
        MyClass myVanishingObject;
        checkHealth = [&myVanishingObject] (int minimumHealth) -> bool {
            std::cout << myVanishingObject.health << std::endl;
            return myVanishingObject.health >= minimumHealth;
        };
    } // myVanishingObject goes out of scope
    // let's do something with the callback to test if myVanishingObject still exists.
    if(checkHealth(4)) {
        std::cout << "has enough healthn";
    } else {
        std::cout << "doesn't have enough healthn";
    }
    return 0;
}

这是输出:

MyClass created!
MyClass destroyed!
5
has enough health

根据cppreference.com网站关于lambda函数的文档

危险参考

如果通过引用(隐式或显式)捕获实体,并且在实体的生存期结束后调用闭包对象的函数调用运算符,则会发生未定义的行为。C++闭包不会延长捕获引用的生存期。

换句话说,您通过引用捕获了对象,然后让对象的生存期结束,这意味着调用lambda会导致未定义的行为。由于UB可能的一种工作方式是"即使对象已经死了,对象看起来仍然活着",我怀疑你看到的是未定义的行为,表现为没有出现任何问题。

我怀疑,如果编译器为临时变量分配了一个唯一的堆栈位置,就会出现这种情况。这意味着在对象的生存期结束后,在main返回之前,内存不会被任何东西触及。因此,您会看到变量和以前一样保持值5,因为它上面没有其他内容

希望这能有所帮助!