我知道delete运算符以及它如何自动调用类的析构函数。但是,我最近看到有人直接调用了类的析构函数,这对我来说似乎很奇怪。所以我写了一个简短的程序,给出了一个非常不期望的结果:
#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'时会崩溃(因为它在2行之前被删除了)。相反......程序只是打印出来而没有任何抱怨:
a:42 b:33
b:33
a:42
......为什么?当我直接调用~A()时会发生什么?有什么情况可以这么做吗?
答案 0 :(得分:9)
手动调用析构函数就像调用函数一样 - 执行代码,但释放内存时不释放内存,而不释放内存。
删除delete
后会导致未定义的行为,并且似乎可以正常工作。
答案 1 :(得分:3)
直接调用~A()时,会执行析构函数的代码。这不会阻止析构函数被通常再次调用,就像C ++一样。例如:
void func()
{
A a;
a.~A(); // calls destructor
// Destructor runs again here as it always would.
}
在您的示例中,将释放int的内存。这只是意味着运行时会记下你的程序不再使用内存......但这并不一定意味着内存会立即被擦除或用于其他内容。因此,当您稍后访问int的内存时,它仍然包含相同的值。但是,有一些其他线程有可能重用这个内存而其他东西可能会覆盖它,但这恰好不会发生在你的程序中。
直接调用~A()很有用的情况是,您希望在某些情况下处理自己的内存分配。例如,您可能拥有自己的字符串类并一次保留大量内存。您可以手动调用构造函数和析构函数来设置此池中的内存区域,以便正确初始化字符串。您可以这样做而无需进行任何新分配 - 只需重用池即可。但是......我会说这是专家级编程,你需要知道你正在做什么,并且这样做是为了获得有价值的性能提升。
答案 2 :(得分:3)
当尝试第二次访问变量'a'时程序应该崩溃
访问已删除的变量是一种未定义的行为。这意味着任何事情都可能发生,包括程序崩溃。
当我直接打电话给A()时会发生什么?
在您的示例中没有,因为您不删除该对象,而是you shouldn't do it。
有什么情况可以这么做吗?
是的,析构函数should be explicitly be called for placement new,这是应该调用析构函数的唯一情况。
答案 3 :(得分:2)
所以如你所见,我正在调用析构函数~A(),所以我希望程序在第二次尝试访问变量'a'时会崩溃(因为它在2行之前被删除了)。相反......程序只是打印出来而没有任何抱怨:
不足为奇。正如您所注意到的,代码是错误的。因此,它不会按照您的预期进行,但难以预测或理解。如果您希望代码具有可预测的行为,则必须遵循规则。这就是他们的目的。
“有人告诉我,在篮球比赛中,你无法控球并且跑动。我得到了一个篮球并尝试了它并且效果很好。他显然不懂篮球。” - 罗杰米勒