【问题标题】:Lambda Capture and Memory ManagementLambda 捕获和内存管理
【发布时间】:2013-02-08 00:03:49
【问题描述】:

当我在 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 health\n";
    } else {
        std::cout << "doesn't have enough health\n";
    }
    return 0;
}

这是输出:

MyClass created!
MyClass destroyed!
5
has enough health

【问题讨论】:

  • 我怀疑这是未定义的行为,在这种情况下,未定义的行为恰好是“对象似乎还活着”。
  • 未定义的行为。见c-faq.com/ansi/experiment.html
  • 如果您通过内存泄漏分析器(例如 Valgrind)运行它,它会(希望)报告您正在访问一个死对象。

标签: c++ memory-management c++11 lambda


【解决方案1】:

根据the cppreference.com website's documentation of lambda functions

悬空引用

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

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

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

希望这会有所帮助!

【讨论】:

  • 那么处理这个问题的好方法是什么?我的建议是为 myVanishingObject(而不是堆栈上的实例)创建一个 shared_ptr,并按值捕获该 shared_ptr。你怎么看?
  • @basteln- 这很有意义 - 您希望对象在创建它的范围之外持续存在,因此堆分配它并使用 shared_ptr 来处理内存管理是合理的。只要确保按值捕获指针!
  • 好的,你的回答很好,但是我们该如何解决这个问题呢?在 Obj-C 和 Swift 中,被 lambdas 捕获的对象被保留(引用计数器递增)并在 lambda 释放后释放
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-12
  • 2011-09-06
  • 2021-01-23
  • 2013-01-17
  • 2011-02-18
  • 2011-07-01
相关资源
最近更新 更多