伪析构函数调用不会销毁对象。

17
请看下面的代码:

考虑以下代码:

#include <iostream>

typedef int t;
t a=42;

int main()
{
    a.t::~t();
    std::cout << a; //42
}

我原本以为a会被销毁。但事实并非如此,为什么?这个伪析构函数调用是如何销毁对象的?


10
如果这个对象被“销毁”,你会有什么期待?在对象的生命周期结束后访问它将产生未定义行为。 - Mankarse
1
请定义“destroyed”一词。您原本期望发生什么或不发生什么? - user3458
@Mankarse 我原本以为a没有指向任何对象。但是a仍然指向一个类型为int且值为42的对象。 - user2953119
1
@DmitryFucintv:嗯...如果它已经被销毁,那么代码将具有未定义的行为。标准对包含未定义行为的代码没有要求,因此,a仍然表示值为42int是代码的一种可能解释(如果代码确实导致a的生命周期结束)。 - Mankarse
1个回答

31

但这不是真的,为什么呢?

§5.2.4/1:

唯一的影响是在点或箭头之前对后缀表达式进行求值。

其中后缀表达式是调用所涉及对象的表达式。因此,伪析构函数调用作为对平凡析构函数的调用,并不会结束它应用于的对象的生存期。例如,

int i = 0;
(i += 5).~decltype(i)();
std::cout << i;

对于标量类型而言,实际上是无法调用析构函数的,因为它们没有析构函数(参见[class.dtor])。这种语句只允许在模板代码中出现,在这种情况下,您可以调用一个类型未知的对象的析构函数——这消除了为标量类型编写专门化的必要性。


评论中指出[expr.pseudo]确实暗示了标量类型具有析构函数:

在点.或箭头->运算符之后使用伪析构函数名称表示所命名的非类类型的析构函数。

然而,这与标准的其他部分不一致,例如第12节将析构函数称为特殊成员函数,并提到:

析构函数用于销毁其类类型的对象。

这似乎是C++98时代产生的一个不精确之处。


2
+1 这是正确的答案。如果扩展标准参考资料(你引用的部分不足以完全理解行为)将会更好。 - Mankarse
1
@DmitryFucintv 已经添加了解释。而且这段代码示例在 Clang 下编译良好,GCC 在这里只是有一个 bug - Columbo
1
标准规定:“在点.或箭头->操作符之后使用伪析构函数名表示类型名称或decltype-specifier所表示的非类类型的析构函数。”这表明该类型确实具有析构函数。(如果它没有析构函数,那么它的析构函数就不能被表示)。然而,在12.4中似乎说非类类型没有析构函数。也许“表示析构函数”的文本只是粗略措辞。 - M.M
2
@Matt 访问 a 是可以的,因为根据 3.8 规定,a 的生命周期并没有结束。措辞可能有些粗糙,是的。等我回来有电脑时,我可能会扩展答案。 - Columbo
5
"伪析构函数"并不存在,伪析构函数调用根本不做任何事情——这个调用是虚假的,而不是析构函数。平凡析构函数就是一种什么也不做的析构函数(即无操作)。 - Columbo
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接