在显式调用时,析构函数被调用两次

时间:2012-08-09 13:08:12

标签: c++ destructor

我正在使用这段代码在C ++中尝试使用析构函数:

#include <iostream>

struct temp
{
    ~temp() { std::cout << "Hello!" << std::endl; }
};

int main()
{
    temp t;
    t.~temp();
}

我看到“你好!”正在打印两次。不应该调用析构函数释放对象,并且当它超出范围时不应该再次调用析构函数吗?或者还有其他一些概念吗?

(我不打算在实践中这样做。我只是想了解这里发生了什么。)

10 个答案:

答案 0 :(得分:40)

这是因为你告诉它发生了。当变量超出范围时,自动变量的析构函数始终被调用。你也叫它。总共有两个电话。

调用一个对象的析构函数不会向C ++发出信号,不再调用它,因为在正常执行中没有必要跟踪它。

解决方案是永远不要手动调用析构函数。

答案 1 :(得分:19)

调用析构函数不会释放对象。

析构函数用于清理对象的内部,然后在析构函数完成后释放对象本身。

执行您正在执行的操作与可以在对象上调用删除两次的方式类似但是这样做是错误的。

只有极少数情况下你想手动调用析构函数,而这不是其中之一。在你使用placement new在内存位置手动构建一个对象时,它确实存在,然后需要能够在不释放内存的情况下对其进行破坏。

答案 2 :(得分:3)

  

我看到“你好!”正在打印两次。不应该调用析构函数释放对象,并且当它超出范围时不应再次调用析构函数。或者还有其他一些概念吗?

这是正确的。

  

我必须提到我不打算在实践中这样做。我只想了解这里发生的事情。

你已经调用了析构函数,准备要销毁的对象。但是,当对象超出范围时,也会在实际取消分配之前自动完成。

要理解的是:如果你做的事情没有意义,那么坏事就会发生。所以不要做那些没有意义的事情。如果手动调用析构函数,则运行descructor。除非析构函数实际上做了有效的事情,否则这对其他任何事都没有影响。

答案 3 :(得分:1)

当对象超出范围时,如果对象在这种情况下位于堆栈中,或者当在堆上使用new运算符在堆上创建对象时使用delete显式销毁,则调用析构函数。的地方。

编译器或运行时系统无法跟踪是否由您手动调用析构函数。对析构函数进行调用也是一种非常糟糕的做法。

如果你想在删除对象之前进行一些手动清理(除了从内存中删除对象或从堆栈中删除对象),你可以这样做。

在这里,您希望允许客户端手动清理内容,甚至在删除对象之前。但除此之外,如果客户不想清理它,你可以清理它。

class A
{
public:
    A() : _closed(false)
    {}

    ~A()
    {
        close();
    }

    void close()
    {
        if (! _closed()) {
            // close file handles etc.
        }
    }

private:
    bool _closed
}

答案 4 :(得分:1)

你只是调用析构函数,你实际上没有释放任何内存(它是静态分配的)。如果你使用new然后删除析构函数只会被调用一次。

答案 5 :(得分:1)

析构函数不是对象的“驱逐舰”。它只是一个普通的函数,但它在被破坏之前就被语言自动调用。

它的官方名称是析构函数,但如果我们将其称为“破坏前”功能,也许会更容易理解。

答案 6 :(得分:1)

尽管可以这样做,但您不需要调用析构函数。当不再使用对象时,编译器应隐式运行析构函数。创建对象时,如果已使用类成员的特定和初始化值声明了对象,则会将该构造函数用于该对象。当您不再需要对象时,析构函数将运行并删除成员变量声明及其值。这对于不使用自动垃圾收集的语言(如C ++)最有用。

答案 7 :(得分:0)

你不应该实际调用解构器。它由运行时支持调用。因此,您只需调用一次,运行时支持就是第二次调用它。

以下是关于析构函数的一些思考:

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fcplr380.htm

您可以显式调用解构器,但不建议这样做,通常会隐式调用它们。

答案 8 :(得分:0)

您没有显式调用析构函数,当变量超出范围时(在return 0;语句之后),它会自动调用。这就是为什么它被调用两次,你调用它,然后系统调用它。

如果您希望能够自己删除此类的实例,则需要动态分配它:

temp *t = new temp;     
// do stuff with t...
delete t;    // do not forget this, or the constructor is not being called at all

答案 9 :(得分:0)

可以调用类的析构函数:

  1. <强>显式

    使用类的对象显式调用析构函数时,与调用类的另一个成员函数的方式相同。

  2. <强>隐式

    当类的对象超出范围或使用delete运算符销毁使用new运算符创建的对象时。

  3. 在您的示例程序中,您同时执行这两项操作

    int main()
    {
      temp t;
    
      t.~temp(); //1. Calling destructor explictly using the object `t`
    
      return 0;
    } // 2. object `t` goes out of scope. So destructor invoked implictly
    

    这就是你看到析构函数被调用两次的原因。

    正如您恰当地想到的,析构函数将破坏构造函数创建的资源。所以不应该明确地调用破坏者,因为它会导致破坏已经被破坏的资源,这可能是致命的。