关于删除通过放置新创建的对象的困惑

Confusion about deleting objects created via placement new

本文关键字:创建 对象 新创建 于删除 删除      更新时间:2023-10-16

我不完全确定下面的代码中发生了什么:

#include <iostream>
struct Foo
{
double dummy{42};
static void* operator new(std::size_t size, void* p)
{
std::cout << R"(Calling placement "operator new" for size )"
<< size << 'n';
return ::operator new(size, p);
}
Foo()
{
std::cout << "Foo::Foo()" << std::endl;
}
~Foo()
{
std::cout << "~Foo::Foo()" << std::endl;
}
};
int main()
{
void* buf_heap = new char[128] {};
Foo* pFoo_heap = new(buf_heap) Foo; // placement allocation
// why does this line call the correct operator delete?
delete pFoo_heap;
// the line above seems to be equivalent to:
// pFoo_heap->~Foo();
// ::operator delete(buf_heap);
}

我知道,每当在构造对象时使用放置new时,都应该手动调用析构函数,然后调用以释放内存(通常通过::operator delete::operator delete[]free,具体取决于放置new的实现方式),请参见例如如何删除通过放置新运算符构造的对象?

然而,在上面的代码中,我创建了一个Foo对象,并将其放置在堆分配的内存中。然后,当我调用delete pFoo_heap时,析构函数会被自动调用(我理解这一点),但内存似乎也会被释放(在这种情况下是buf_heap)。也就是说,如果我尝试之后做::operator delete[](buf_heap);,我会得到一个segfault。

所以基本上线

delete pFoo_heap;

似乎相当于

pFoo_heap->~Foo(); 
::operator delete[](buf_heap);

事实确实如此吗(或者只是UB)?为什么buf_heap中分配的内存被取消分配?或者,换句话说,即使通过放置new分配,delete pFoo_heap;是否知道内存来自哪里?

您问:

// why does this line call the correct operator delete?
delete pFoo_heap;
// the line above seems to be equivalent to:
// pFoo_heap->~Foo();
// ::operator delete(buf_heap);

这不是真的。您的代码受到未定义行为的影响。来自C++标准:

5.3.5删除

2如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并在本节剩余部分使用转换后的操作数代替原始操作数。在第一种选择(删除对象)中,delete的操作数的值可以是空指针值、指向由以前的新表达式创建的非数组对象的指针,或者指向表示此类对象基类的子对象(1.8)的指针(第10条)。如果不是,则行为未定义。

在您的案例中,pFoo_heap不是由新表达式创建的buf_heap是由new表达式创建的。您需要使用:

pFoo_heap->~Foo();
delete [] buf_heap;

对于一个表现良好的程序。

为什么buf_heap中分配的内存被取消分配?

因为您正在取消分配它。请注意,buf_heap == pFoo_heap,所以当您执行delete pFoo_heap时,它恰好与您开始执行new Foo时一样工作-您正在删除的指针指向以前使用new分配的内存。所以它…"有效"。。。