在为对象调用析构函数时,它被调用两次

On calling destructor for an object, it is called two times?

本文关键字:调用 两次 对象 析构函数      更新时间:2023-10-16

显式调用desctructor时,它会执行两次。这是什么原因呢?

#include <iostream>
using namespace std;
class A
{
    public:
        int x;
        A() { cout << "A's constructor called " << endl;  }
        ~A(){
            cout<<"A's desctructor called "<<endl;
        }
};
int main()
{
    A a;
    A b;
    a.~A();
}

输出:

A 的构造函数称为
A的解构器称为
A的解构器称为

好吧,你用"a"调用它,然后当对象超出范围时,"语言"再次调用它为"a"。然后,当然,"语言"称它为b。当然,我所说的"语言"是指最基本的规则,即自动作用域对象在其作用域初始化时构造,并在作用域结束时析构。

对析构函数使用显式调用很少是一个好主意。

你不应该手动调用析构函数,当对象超出范围时,它会自动调用。

手动调用析构函数

的唯一位置是编写自己的分配器时,但这是一个相当高级的主题,因此经验法则是永远不要手动调用析构函数。

[basic.life]/8:

如果程序使用 [...] 结束类型 T 对象的生存期 自动 (3.7.3( 存储持续时间,如果T具有非平凡 析构函数,程序必须确保一个对象的原始 类型在隐式析构函数时占用相同的存储位置 通话发生;否则,程序的行为是未定义的。

因此,我们无法解释程序的一般行为,但我们可以说"对于您的实现,特定的执行行为就像析构函数被调用了两次一样。

当变量超出范围时,将隐式调用其析构函数。

如果那里没有适当类型的对象,则行为是未定义的。 通常,编译器生成的代码将调用析构函数并盲目地这样做,因为这会使"定义的行为"路径高效而简单,而"未定义的行为"路径被认为是您的错。

所以你所看到的是未定义行为的症状。 调用析构函数并不意味着编译器不会尝试销毁对象。

事实上,如果你的对象稍微复杂一些,它很容易导致崩溃。

不要直接调用对象的析构函数,除非您使用了放置 new(构造对象但不分配任何内存的 new 变体(或等效项,并且不要使用 place new,除非您真的知道自己在做什么。

还有另一种有效的用途,即在同一个地方破坏和重建,但这是危险的,通常是一个坏主意,而且很难正确。