1

我知道删除运算符以及它如何自动调用类的析构函数。然而,我最近看到有人直接调用一个类的析构函数,这对我来说似乎很奇怪。所以我写了一个简短的程序,它给出了一个非常出乎意料的结果:

#include <stdio.h>

class A
{
public:
  A()  {a = new int; *a=42; b=33;}
  ~A() {delete a;}

  int* a;
  int  b;
};

int main(int argc, const char ** argv)
{
  A* myA = new A();
  printf("a:%d  b:%d\n", *(myA->a), myA->b);
  myA->~A();
  printf("b:%d\n", myA->b);
  printf("a:%d\n", *(myA->a));
}

正如你所看到的,我调用了析构函数~A(),所以在我的预期中,程序在第二次尝试访问变量“a”时应该会崩溃(因为它在两行前被删除了)。相反..程序只是打印这个没有任何抱怨:

a:42  b:33
b:33
a:42

... 为什么?当我直接调用 ~A() 时会发生什么?在任何情况下这样做有用吗?

4

4 回答 4

10

手动调用析构函数就像调用一个函数——代码被执行,但内存没有被释放,与调用相反delete,当内存被释放时。

删除后访问a将导致未定义的行为,并且看起来可以正常工作。

于 2013-01-08T12:40:32.457 回答
5

尝试第二次访问变量“a”时程序应该崩溃

访问已删除的变量是未定义的行为。这意味着任何事情都可能发生,包括程序崩溃。

当我直接调用 ~A() 时会发生什么?

在您的示例中什么都没有,因为您没有删除该对象,但您不应该这样做

在任何情况下这样做有用吗?

是的,应该为放置 new 显式调用析构函数,这是唯一应该调用析构函数的情况。

于 2013-01-08T12:48:26.453 回答
3

当你直接调用 ~A() 时,会执行析构函数的代码。这不会像 C++ 那样阻止析构函数再次被调用。例如:

void func()
{
   A a;
   a.~A();  // calls destructor
   // Destructor runs again here as it always would.
}

在您的示例中,将释放 int 的内存。这只是意味着运行时会记下您的程序不再使用内存......但这并不一定意味着内存会立即被擦除或用于其他用途。因此,当您稍后访问 int 的内存时,它仍然包含相同的值。但是,其他一些线程有可能重用了这个内存,而其他东西可能会覆盖它,但这恰好不会发生在你的程序中。

直接调用 ~A() 很有用的一种情况是,您想为某些要优化的情况处理自己的内存分配。例如,您可能拥有自己的字符串类并一次性保留大量内存。您可以手动调用构造函数和析构函数来将此池中的内存区域设置为正确初始化的字符串。您可以这样做而无需进行任何新的分配 - 只需重用池。但是...我会说这是专家级编程,您需要知道自己在做什么,并且这样做是为了获得有价值的性能提升。

于 2013-01-08T12:46:16.100 回答
2

正如你所看到的,我调用了析构函数~A(),所以在我的预期中,程序在第二次尝试访问变量“a”时应该会崩溃(因为它在两行前被删除了)。相反..程序只是打印这个没有任何抱怨:

不奇怪。正如您所注意到的,代码有问题。所以它不会做你期望的事情,而是一些难以预测或理解的事情。如果您想要行为可预测的代码,则必须遵守规则。这就是他们的目的。

“有人告诉我,在篮球中你不能拿着球跑。我拿了一个篮球试了一下,效果很好。他显然不懂篮球。” ——罗杰·米勒

于 2013-01-08T12:41:15.563 回答