23

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

#include <iostream>

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

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

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

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

4

10 回答 10

43

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

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

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

于 2012-08-09T13:11:58.690 回答
20

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

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

执行您正在执行的操作类似于您可以在一个对象上调用两次 delete 的方式是错误的,但这样做是错误的。

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

于 2012-08-09T13:13:43.020 回答
3

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

这是正确的。

我必须提到,我不打算在实践中这样做。我只是想了解这里发生了什么。

你已经调用了析构函数,准备一个要被销毁的对象。但这也是在对象超出范围时自动完成的,在它实际被解除分配之前。

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

于 2012-08-09T13:18:44.477 回答
1

如果对象在堆栈中(如本例所示),则当对象超出范围时调用析构函数,或者在使用 new 运算符在堆上创建对象时使用 delete 显式销毁它时调用。

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

如果您想在对象被删除之前进行一些手动清理(除了从内存中删除或从堆栈中删除的对象),您可以执行类似的操作。

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

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

    ~A()
    {
        close();
    }

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

private:
    bool _closed
}
于 2012-08-09T13:15:33.310 回答
1

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

于 2012-08-09T13:18:52.257 回答
1

析构函数不是对象的“破坏者”。它只是一个普通的函数,但在销毁之前由语言自动调用。

它的正式名称是析构函数,但如果我们称它为“Before-Destruction”函数可能会更容易理解。

于 2012-08-09T21:00:55.030 回答
1

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

于 2012-08-13T12:52:00.610 回答
0

您实际上不应该调用解构器。运行时支持会为您调用它。因此,您调用它一次,运行时支持将第二次调用它。

这里有一些关于析构函数的思考:

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

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

于 2012-08-09T13:13:54.867 回答
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
于 2012-08-09T13:14:17.703 回答
0

可以调用类的析构函数:

  1. 明确地

    当使用类的对象显式调用析构函数时,就像调用类的另一个成员函数一样。

  2. 隐式

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

在您的示例程序中,您同时执行

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

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

正如您所想的那样,析构函数将破坏由构造函数创建的资源。所以不应该显式调用析构函数,因为它会导致销毁已经销毁的资源,这可能是致命的。

于 2012-08-13T13:37:44.780 回答