Far*_*nor 4 c++ destructor placement-new
语境
我正在尝试掌握新的放置机制,因为我从未使用过它。出于纯粹的好奇心,我试图了解如何正确使用它。
对于这个问题,我们将考虑以下代码库来进行说明:
struct Pack
{
int a, b, c, d;
Pack() = default;
Pack(int w, int x, int y, int z) : a(w), b(x), c(y), d(z)
{}
~Pack()
{
std::cout << "Destroyed\n";
}
};
std::ostream & operator<<(std::ostream & os, const Pack & p)
{
os << '[' << p.a << ", " << p.b << ", " << p.c << ", " << p.d << ']';
return os;
}
Run Code Online (Sandbox Code Playgroud)
基本上它只是定义了一个保存一些数据的结构(示例中为 4 个整数),我重载了operator<<()来简化测试部分的代码。
我知道,当使用placement-new时,必须手动调用对象的析构函数,因为我们不能使用它,delete因为我们只想销毁该对象(而不是释放内存,因为它已经分配了)。
示例(A):
char b[sizeof(Pack)];
Pack * p = new (b) Pack(1, 2, 3, 4);
std::cout << *reinterpret_cast<Pack*>(b) << '\n';
p->~Pack();
Run Code Online (Sandbox Code Playgroud)
问题
我想知道,当对同类型的对象使用placement-new时,是否还需要调用该对象的析构函数?
示例(B):
Pack p;
new (&p) Pack(1, 2, 3, 4);
std::cout << p << '\n';
Run Code Online (Sandbox Code Playgroud)
我在这里对两个版本进行了快速测试,似乎当底层对象在类型为 时超出范围时,析构函数被正确调用Pack。
我知道在这样一个简单的例子中使用placement-new而不是直接创建对象是愚蠢的。但在实际例子中,它可以是一个缓冲区,也Pack可以代替char.
我想知道我是否正确地假设我不需要手动调用示例(B)中的析构函数,或者我是否遗漏了某些内容。
额外的
作为一个附属问题,在示例(A)中,删除返回的指针并通过reinterpret_casted 指针调用析构函数是否合法?
例如可以这样重写:
char b[sizeof(Pack)];
new (b) Pack(1, 2, 3, 4);
std::cout << *reinterpret_cast<Pack*>(b) << '\n';
reinterpret_cast<Pack*>(b)->~Pack(); // Is it legal ?
Run Code Online (Sandbox Code Playgroud)
如果我有一个对象缓冲区并且我想销毁它的元素而不必将返回的指针保留在某处,那么这将很有用。
[免责声明]:当然,在实际的程序中,我会使用 astd::vector和emplace_back()函数而不是我自己的缓冲区。正如已经提到的,这个问题纯粹是出于好奇,只是为了了解它是如何在幕后工作的。
是的,您必须调用析构函数。旧对象是否属于同一类型无关紧要。
唯一重要的是旧对象的类型是否是可轻易破坏的。如果是,则无需调用析构函数。如果不是,则必须在重用内存之前调用析构函数。
例子:
Pack p;
new (&p) Pack(1, 2, 3, 4); // Not OK
p.~Pack();
new (&p) Pack(1, 2, 3, 4); // OK
Run Code Online (Sandbox Code Playgroud)
请注意,在某些情况下这是不允许的,例如类包含 const 限定成员或引用成员。一般来说,我建议避免这种模式,而只重用 char 数组或类似的简单存储。
作为一个附属问题,在示例(A)中,摆脱返回的指针并通过reinterpret_casted指针调用析构函数是否合法?
就像放置新对象的所有使用一样,您可以重新解释原始对象的地址,但必须对其进行清洗:
Pack* ptr = new (b) Pack(1, 2, 3, 4);
std::cout << *ptr << '\n'; // OK
std::cout << *reinterpret_cast<Pack*>(b) << '\n'; // Not OK
std::cout << *std::launder(reinterpret_cast<Pack*>(b)) << '\n'; // OK
ptr->~Pack(); // OK
reinterpret_cast<Pack*>(b)->~Pack(); // Not OK
std::launder(reinterpret_cast<Pack*>(b))->~Pack(); // OK
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)char b[sizeof(Pack)]; Pack * p = new (b) Pack(1, 2, 3, 4);
这是错误的。您必须确保存储与新放置的类型正确对齐:
alignas(alignof(Pack)) char b[sizeof(Pack)];
Run Code Online (Sandbox Code Playgroud)