【问题标题】:c++ destructor called too earlyc++ 析构函数调用太早
【发布时间】:2014-12-08 02:18:59
【问题描述】:

我已将代码简化为仍然存在问题的最小示例。此代码应打印“42”,而是打印不同的数字。我还在析构函数中打印了“秘密”对象的地址,当它被访问时,表明它被过早地破坏了。我在这里做错了什么,还是编译器有问题?

代码:

#include <iostream>    

using namespace std;    

struct Secret{
    int value;
    Secret(int value):value(value){}
    ~Secret(){
        cout<<"destructor:"<<(void*)this<<endl;
        value=0;
    }
};    

template<class Func>
class Copier{
public:
    Func func;
    Copier(Func func):func(func){}
    void run(){
        func();
    }
    auto copy(){
        auto output = [this](){
            func();
        };
        Copier<decltype(output)> out(output);
        return out;
    }    

};
auto makeSecretPrinter(){
    Secret secret(42);
    auto secretPrinter = [secret](){
        cout<<"reading object at address:"<<(void*)&secret<<endl;
        cout<<"the secret is:"<<secret.value<<endl;
    };
    return Copier<decltype(secretPrinter)>(secretPrinter).copy();
}    

int main(){
    makeSecretPrinter().run();
    return 0;
}

clang(版本 3.5-1ubuntu1)输出:

destructor:0x7fff9e3f9940
destructor:0x7fff9e3f9938
destructor:0x7fff9e3f9948
destructor:0x7fff9e3f9950
reading object at address:0x7fff9e3f9940
the secret is:0

GCC (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2 输出:

destructor:0x7fff374facc0
destructor:0x7fff374facb0
destructor:0x7fff374faca0
destructor:0x7fff374fac90
reading object at address:0x7fff374facc0
the secret is:-1711045632

【问题讨论】:

  • 捕获this意味着用指针语义捕获。如果捕获的对象被销毁,您可能不会取消引用该指针(甚至不能隐式取消引用,就像在copyoutput lambda 中所做的那样)。复制该 lambda 会复制捕获的指针,即执行浅拷贝。
  • auto output = [this](){ 这行似乎是问题所在。你真的想复制*this 而不是指针
  • @BryanChen 跟我想的一模一样:coliru.stacked-crooked.com/a/43b70f00c4a20469
  • 感谢@BryanChen,我没有意识到可以在 lambda 中捕获 *this。效果很好!
  • 程序有问题。请参阅下面的答案

标签: c++ gcc c++14 clang++


【解决方案1】:

使用指针语义捕获this。改变这个:

auto output = [this](){
    func();
};

到此解决问题:

auto output = [self=*this](){
    self.func();
};

【讨论】:

    【解决方案2】:

    这一行:makeSecretPrinter().run(),实际上是先执行makeSecretPrinter(),再执行run()。但是,当run() 正在执行时,它已经超出了makeSecretPrinter() 的范围。因此 lambda 函数没有得到正确的值作为要调用的参数。

    请注意,您的类仅接收函数指针(或函数引用,它们在语义上是相同的)。这意味着该值不会传递给Copier 类。当Copier 尝试'run()' 时,它需要获取makeSecretPrinter() 中的变量。但是,正如我所说,在执行 'run()' 时它超出了范围。

    【讨论】:

      猜你喜欢
      • 2014-05-03
      • 2021-07-19
      • 2017-04-28
      • 2013-04-17
      • 1970-01-01
      • 1970-01-01
      • 2012-07-12
      • 2016-10-28
      • 2019-01-09
      相关资源
      最近更新 更多