48

我知道在大多数情况下,我们不应该显式调用析构函数。但是,我从 C++11 标准 N3485 第 13.4.5 节模板参数中看到了一个示例:

对具有类模板特化类型的对象的显式析构函数调用可以显式指定模板参数。例子:

template<class T> struct A {
    ~A();
}; 

void f(A<int>* p, A<int>* q) {
    p->A<int>::~A();      // OK: destructor call
    q->A<int>::~A<int>(); // OK: destructor call
}

在我看来,在这种情况下我们可以显式调用析构函数,你能解释一下为什么吗?在这个例子中,这些析构函数调用是什么意思?为什么它们是合理的?

另一个问题:

除了在实现时,我们还可以在哪些情况下显式调用析构函数placement delete

谢谢你。

编辑:我从C++ FAQ中发现我们不应该在局部变量上显式调用析构函数。

4

2 回答 2

34

在我看来,在这种情况下我们可以显式调用析构函数,你能解释一下为什么吗?

你的意思是我们为什么可以?因为该语言允许对任何对象进行显式析构函数调用。正如您所说,它通常会给出未定义的行为,因为大多数对象将以其他方式被销毁,并且两次销毁任何东西(或更一般地在销毁后访问它)是未定义的行为。但这只是意味着你不能这样做,而不是语言会阻止你这样做。

或者你的意思是我们为什么要这样做?因为这就是你如何销毁由placement new 创建的对象。

在这个例子中,这些析构函数调用是什么意思?

它们都表示相同的意思,并且等同于p->~A(); 他们调用对象的析构函数。该示例演示了如果您愿意,可以在此处提供模板参数。我不知道你为什么想要。

除了放置删除之外,我们还可以在哪些情况下显式调用析构函数?

认为您可以随时调用微不足道的析构函数(不做任何事情的析构函数);但没有意义。我认为破坏新位置创建的东西是这样做的唯一正当理由。

于 2013-05-23T17:46:30.947 回答
29

在我看来,在这种情况下我们可以显式调用析构函数,你能解释一下为什么吗?

因为语言允许它可以随时调用任何对象的析构函数(假设您有访问权限,例如它不是私有析构函数)。

在这个例子中,这些析构函数调用是什么意思?

它只是调用析构函数。从逻辑上讲,这意味着该对象已被破坏,从那时起应该被视为垃圾,不应取消引用或使用。从技术上讲,这意味着对象处于析构函数保留它的任何状态,这对于某些对象可能与默认构造相同(但您永远不应该依赖它)。

为什么它们是合理的?

有时您需要在不释放内存的情况下销毁对象。这发生在很多类中,比如变体/任何、各种脚本绑定和反射系统、一些单例实现等。

例如,您可以使用std::aligned_storage为一个对象分配一个缓冲区,然后使用placement new 在该缓冲区中构造一个对象。你不能调用delete这个对象,因为它会调用析构函数并尝试释放支持它的内存。在这种情况下,您必须显式调用析构函数才能正确销毁对象。

除了放置删除之外,我们还可以在哪些情况下显式调用析构函数?

除了放置 new 的相应运算符之外,实际上并没有诸如“放置删除”之类的东西(并且任何调用都delete将隐式调用析构函数,除非编译器为构造失败而调用的那些,例如您的“放置删除”概念)。

我在上面给出的一个例子。另一个例子是std::vector。您可以调用成员函数,例如pop_back(). 这需要破坏向量中的最后一个元素,但它不能使用delete,因为支持对象的内存是必须单独管理的更大缓冲区的一部分。许多其他容器也是如此,例如开放寻址哈希表,deque等等。这是您想要使用template typename来显式调用析构函数的示例。

这是一个库的用户很少需要的功能,但是像 STL 甚至一些应用程序框架这样的低级库的实现者将需要在这里和那里使用。

于 2013-05-23T18:15:17.967 回答