为什么析构函数会在这里被调用两次?

时间:2016-05-25 09:49:35

标签: c++ struct destructor

#include <iostream>

struct ABC{
    int A;
    ABC(int i = 1) : A(i) {}
    ~ABC() {
        std::cout << A << std::endl;
    }
    void destruct() {
        delete this;
    }
};

int main() {
    ABC A1(2);
    A1.destruct();
    return 0;
}

Output:
2
2

我有这个代码,我试图手动删除结构变量。这样做,我意识到析构函数在这里被调用了两次。为什么会这样?为什么在调用destruct()时不会删除它?

4 个答案:

答案 0 :(得分:6)

delete this来电导致undefined behaviour,这意味着任何事情都可能发生。

delete只能用于由new创建的对象。

对于具有非平凡析构函数的自动对象(例如,您的A1),不可能及早销毁它们&#34;除非您在范围结束之前在同一位置创建另一个ABC。换句话说,你不能关闭&#34;范围结束时发生的销毁过程。

答案 1 :(得分:2)

这是RAII工作加上你对一个物体自杀。在对象上调用析构函数几乎总是错误的!并且两次调用析构函数总是错误的,因为它调用了未定义的行为。

你必须明白C ++正在为你处理内存,如果你只是让它:

struct Foo{};
int main() {
    Foo f;               // automatic storage, gets destroyed
                         // when object gets out of scope

    Foo* g = new Foo();  // heap allocated
    delete g;            // only here you have to delete
}

请记住:不要delete通过new创建的任何内容(感谢Mike Vine的评论)。除非你需要,否则不要使用(裸)堆分配。

答案 2 :(得分:2)

  

为什么在调用destruct()时不会删除[我的对象]?

当一个对象在C ++中被破坏时,并不意味着该对象消失。这意味着析构函数中的清理代码会被执行,并且从该点开始对对象成员的任何访问都是无效的。

当您致电destruct()时,您会尝试通过拨打delete来释放对象的内存。这本身就是未定义的行为,因为您尚未使用new分配对象。此调用会导致第一次打印输出。

但是,由于您的对象位于自动内存中,因此当对象超出范围时,需要C ++调用其析构函数。这是导致第二次打印输出的调用。

注意:您可以通过在动态内存中分配A1来修复代码:

int main() {
    ABC *A1 = new ABC(2);
    A1->destruct();
    return 0;
}

现在你得到一个打印输出(demo)。但是,将delete隐藏在成员函数中的做法是值得怀疑的。

答案 3 :(得分:1)

这里要考虑两点: -

1)堆栈对象的析构函数在超出范围时将始终被调用。所以不必担心他们的解除分配。

2)你不能&amp;不应该在堆栈上分配的对象上使用delete。一般情况下,只要您不确定仅在删除堆对象的情况下执行此操作,并且之后您没有引用该对象,就不应该使用delete this