【问题标题】:On calling destructor for an object, it is called two times? [duplicate]在为对象调用析构函数时,它被调用了两次? [复制]
【发布时间】:2014-11-30 03:36:31
【问题描述】:

在显式调用析构函数时,它会执行两次。这是什么原因?

#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,第二个用于b
  • 除了误认为作用域中的任何对象都被析构函数清理之外,没有其他原因。
  • 我在上面看到了未定义的行为:任何事情都可以发生!

标签: c++ destructor


【解决方案1】:

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

显式调用析构函数并不是一个好主意。

【讨论】:

  • 我相信b的析构函数没有被调用,因为b没有被使用,甚至没有被构造。
【解决方案2】:

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

手动调用析构函数的唯一地方是在编写自己的分配器时,但这是一个相当高级的话题,所以经验法则是永远不要手动调用析构函数。

【讨论】:

  • 这不是手动调用析构函数的唯一情况。例如,boost::variant 会这样做。
  • @Yakk 类似于单个对象的分配器(放置新)
【解决方案3】:

[basic.life]/8:

如果程序以 [...] 结束 T 类型对象的生命周期 自动(3.7.3)存储持续时间,如果T 有一个非平凡的 析构函数,程序必须保证原来的对象 当隐式析构函数时,类型占用相同的存储位置 调用发生; 否则程序的行为是不确定的。

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

【讨论】:

  • 不完全是:解释确实是“存在未定义的行为,因为在 a 中不再存在 A 对象,因为它已被销毁。今天,未定义的行为看起来像一个析构函数调用。”
  • @Yakk 改编了你的。现在怎么样了?
  • 我不应该添加关于标准的评论线程。
【解决方案4】:

当一个变量超出范围时,它的析构函数被隐式调用。

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

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

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

不要直接调用对象的析构函数,除非你使用了placement new(new 的一个变体,它构造一个对象并且不分配任何内存)或它的等价物,并且不要使用placement new,除非你真的知道你在做什么。

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

【讨论】:

    猜你喜欢
    • 2022-07-05
    • 1970-01-01
    • 1970-01-01
    • 2018-07-06
    • 2012-08-06
    • 2013-01-12
    • 2016-11-23
    • 2015-02-25
    相关资源
    最近更新 更多