即使在相同类型上,也应该以新的位置调用析构函数

时间:2019-03-18 15:53:27

标签: c++ placement-new

#include <new>

struct X
{
    ~X() { std::cout << "destroyed" << std::endl; }
    int x;
};

int main(int argc, const char * const * const argv)
{
    X x{1};

    new (&x) X{2};

    std::cout << x.x << std::endl;

    return 0;
}

输出

2
destroyed

我知道的是,在使用placement new时应始终调用析构函数。但是,在此示例代码中,析构函数在main的末尾隐式调用,因此再次调用它是未定义的行为。 所以现在我想知道在使用placement new时是否应该始终调用析构函数,或者在某些条件下不应该调用析构函数?

2 个答案:

答案 0 :(得分:2)

  

我知道的是,在使用placement new时应始终调用析构函数。

是的,除非类型是微不足道的。

在这种情况下,必须在放置新位置之前销毁先前构造的对象:

X x{1};
x.~X();
try {
    new (&x) X{2};
} catch(...) {
    std::abort(); // no way to recover
}

不可破坏类型的自动变量在破坏状态下一定不能超出范围。如果构造函数抛出异常,则该行为将是不确定的。不建议重用非平凡对象的内存。

重用琐碎对象的内存更安全:

alignas(alignof(X)) std::byte arr[sizeof(X)];
new (arr) X{2};
x.~X();

答案 1 :(得分:2)

它在C ++标准中明确指定

  

[基本生活]

     

5程序可以通过重用   对象占用的存储空间或通过显式调用   具有非平凡的类类型的对象的析构函数   析构函数。对于具有非平凡的类类型的对象   析构函数,不需要程序调用析构函数   在对象占用的存储之前明确地重用或   释放但是,如果没有显式调用析构函数或   如果不使用delete-expression释放存储,则   不应隐式调用析构函数,并且依赖于任何程序   析构函数产生的副作用具有不确定的行为。

最后一句话为不确定行为 1 的可能性留出了一些麻烦的余地。但最终,唯一可以明确定义的类型是那些析构函数真正无关紧要的类型。


1 -无论如何,在撰写本文时。