C++ 对使用运算符new的placement delete的结果调用delete是否正确?

C++ 对使用运算符new的placement delete的结果调用delete是否正确?,c++,new-operator,delete-operator,placement-new,C++,New Operator,Delete Operator,Placement New,如果我这样做 struct MyStruct{~MyStruct(){}; void*buffer=运算符new(1024); MyStruct*p=new(buffer)MyStruct(); // ... 删除p;// [修订版]它通常不正常,您只能大致删除使用匹配的普通新表达式获得的内容。否则,由您来保证解除分配函数与分配函数匹配(因此使用::delete p;将是更安全的通用解决方案,假设您的原始操作符new位于全局命名空间中)。特别是当所讨论的类(或其派生类之一)重载操作符new时,您

如果我这样做

struct MyStruct{~MyStruct(){};
void*buffer=运算符new(1024);
MyStruct*p=new(buffer)MyStruct();
// ...

删除p;// [修订版]它通常不正常,您只能大致
删除使用匹配的普通
表达式获得的内容。否则,由您来保证解除分配函数与分配函数匹配(因此使用
::delete p;
将是更安全的通用解决方案,假设您的原始
操作符new
位于全局命名空间中)。特别是当所讨论的类(或其派生类之一)重载
操作符new
时,您必须小心

由于您正在使用placement new表达式创建
*p
,并且没有“placement delete表达式”,因此必须手动销毁对象,然后释放内存:

struct MyStruct { ~MyStruct() { } };

void *buffer = operator new(1024);
MyStruct *p = new(buffer) MyStruct();
// ...
delete p;     //    <---------- is this okay?

delete p
相当于

p->~MyStruct();

operator delete(buffer);
除非
MyStruct
有一个可选的
操作符delete
,否则您的示例应该使用预期的语义进行良好定义

[expr.delete]/2
说明:

delete的操作数的值可能是。。。指向由以前的新表达式创建的非数组对象的指针

Placement new是一种新表达式<代码>[expr.new]/1

新表达式:
::opt new placement opt new type id new initializerpt
::opt新建位置opt(类型id)新建初始值设定项opt

delete
定义为调用对象的析构函数,然后调用内存的释放函数<代码>[expr.delete]/6,7

。。。delete表达式将调用对象的析构函数(如果有)

。。。删除表达式将调用解除分配函数


只要释放函数与分配函数匹配(它应该匹配,只要你没有为你的类重载
操作符delete
),那么这一切都应该得到很好的定义。

不,你一般不应该调用delete(在某些情况下,比如操作符delete被覆盖时,它可能没问题)


在分配和构造对象的方式与一步完成的方式之间,产生的内存表示是否存在差异?如果是,则删除可能会失败。如果否,则删除可能会成功。因此,问题归结为标准如何规定
new
,而不是
delete
。我用gcc+Valgrind进行了测试,没有报告任何错误。除了下意识的“这很奇怪,与内存有关,所以一定是UB”,你对此有什么证据?@Mankarse:
delete p
可能会调用
MyStruct::operator delete
,这可能会做一些完全不合适的事情,而实际调用的是
::operator delete
。是的,但这并不意味着您只能
删除
通过普通
表达式获得的内容。它也可以在您知道不存在
MyStruct::operator delete
的情况下工作。(尽管我同意这使得使用
delete
有些不安全,并且在大多数情况下可能是个坏主意)。@Mankarse:5.3.5/2:“
delete
的操作数的值可能是[…]指向由以前的新表达式[…]创建的非数组对象的指针。如果不是,则行为是未定义的。”@Mehrdad:但请注意,这种设计是不安全的:通过稍后向类中添加一些内容,有人可能会远程破坏您的代码,而您永远不会知道。只要做正确的事,你就可以省去所有这些麻烦。(实际正确的做法是无论如何都不要在客户端代码中拼写“new”,而是使用类似分配器和
allocate\u shared
等)嗯。您还必须注意,实现不允许在
操作符new
的返回值之后添加填充。(不是;C++11§5.3.4/10。)即使如此,我仍然对此感到不安。这似乎不是一个不可避免的用例,因为
dynamic\u cast()
即使在不知道动态类型的情况下也会为您获取最派生的指针。毕竟,placement new并不要求首先从
::operator new()
获取内存。这个答案的其余部分立即崩溃,消除了基本的误解。
p->~MyStruct();
operator delete(p);
char *buffer = new char[1024];
MyStruct *p = new(buffer) MyStruct(); //placement new "i'm just calling constructor"
p->~MyStruct();  //destroy the object.

//delete p; // WHAT? even if you override operator delete it may be opaque to reader.
delete [] buffer; // THIS DELETE IS OK (if you have first destroyed MyStruct)