【问题标题】:Why an object is destroyed more than one time? [c++11]为什么一个对象会被多次破坏? [c++11]
【发布时间】:2016-09-09 11:32:30
【问题描述】:

在这个小例子中,一个对象被多次破坏...

可以在以下位置找到输出:http://ideone.com/ddJ6Hh

#include <iostream>
#include <string>
#include <memory>
#include <functional>

using namespace std;
struct sA
{
    static int n;
    int id;
    string name;
    sA(string s="default"):id(++n),name(s)  {   cout<<"object '"<<name<<"' created\t  id="<<id<<"\taddr="<<this<<"\tn="<<n<<"\n";   }
    ~sA()   {   cout<<"object '"<<name<<"' destroyed\t  id="<<id<<"\taddr="<<this<<"\tn="<<--n<<"\n";   }

//  sA(const sA& other):id(++n),name(other.name+"_Copy")    {   cout<<"object '"<<name<<"' copied\t  id="<<id<<"\taddr="<<this<<"\tn="<<n<<"\n";    }
    sA operator=(const sA& other)   =delete;
};
int sA::n;




int main()
{
    function<void(void)> vp;

    {
        sA f("1___This Object will die when others need it...");
        sA b("2___This Object will not die ...");
        vp=[&f,b]
        {
            cout<<"Hello\n";
            cout<<b.name<<"||\t addr="<<&b<<"\t|<*>| Stack Data is ok\n";
            cout<<f.name<<"||\t addr="<<&f<<"\t|<*>| Stack Data may not be there\n";
        };
        vp();
        cout<<"********* Block end\n";
    }

    sA k("3__test obj");
    vp();
    return 0;
}

到目前为止,我想出的可能原因是:

1) lambda 获取的本地对象的引用超出了活动范围。[for object 'f']

2) 调用默认拷贝构造函数,旧拷贝被销毁 [for object b]

但这些原因并不能回答以下事实:

1) 即使在最里面的块结束之前,两个对象也被破坏了。 [区块结束识别]

2) 最多必须有 1 个额外的破坏,但它显示 3。[作为程序末尾的 n=-3]

3) 如果我以递归方式启用复制构造函数,则正在创建副本,但最后“构造的总数=破坏的总数”。 [在程序结束时 n=0]

这是 lambda(不可变)的行为吗?

或者它只是代码中的另一个错误?

还有什么是 lambda 及其捕获的变量的生命周期?

【问题讨论】:

  • 此段错误(其他地方)因为您在 lambda vp=[&amp;f,b] 中通过引用 f 捕获,然后尝试在外部块范围之后使用 vp() 再次调用它,因为引用不再有效.你调用了未定义的行为。
  • 你的对象没有被销毁 2 次它的副本被销毁,因为在 clusore b 被值捕获
  • 如果在移动和复制构造函数中增加n(自动生成的构造函数不会这样做),n 最终将变为 0。
  • 提示:将cout&lt;&lt;this 添加到您的构造函数和析构函数中。
  • cout

标签: c++ function c++11 lambda scope


【解决方案1】:

您的 lambda 对象通过引用本地对象 f 捕获,该对象在您的“块结束”打印之后超出范围(因此被销毁)。因此,您在第二次调用 vp() 时有一个悬空引用,这是未定义的行为。

【讨论】:

  • f 不会被销毁两次,b 会。
  • @TartanLlama - 同意f 没有被破坏两次,删除了那部分。不确定我是否同意 b 是 - 不一样的 b 反正
  • 是的,不一样的b,它的副本。
【解决方案2】:

假设您修复了代码中未定义的行为,则有些副本并不立即显而易见。

vp=[&f,b]
{
    //...
};

该声明中实际上有三个b 的副本。

  1. 复制到 lambda
  2. 复制到std::function构造函数的参数中
  3. 复制到std::function的内部存储

前两个副本将在语句结束时销毁,因此您会看到两个销毁。被销毁两次的不是同一个对象。

std::function 必须进行类型擦除,所以它实际上可能非常昂贵。您可以使用合理的移动构造函数来缓解这种情况,但通常最好尽可能使用原始 lambda 类型。

【讨论】:

    猜你喜欢
    • 2014-09-17
    • 2014-05-26
    • 1970-01-01
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 2022-01-23
    相关资源
    最近更新 更多