【问题标题】:Is calling delete on the result of a placement delete which used operator new okay?对使用 operator new 的放置 delete 的结果调用 delete 好吗?
【发布时间】:2013-05-19 12:18:42
【问题描述】:

如果我这样做

struct MyStruct { ~MyStruct() { } };

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

delete 是否保证会照顾 both 调用 ~MyStruct() 以及 operator delete

【问题讨论】:

  • 您分配和构造对象的方式与一步完成的方式之间的结果内存表示是否存在差异?如果是,那么删除可能会失败。如果否,则删除可能会成功。所以问题归结为标准如何指定new的操作,而不是delete
  • 我用 gcc+Valgrind 对此进行了测试,没有报告任何错误。

标签: c++ new-operator delete-operator placement-new


【解决方案1】:

delete p 等价于

p->~MyStruct();
operator delete(p);

除非MyStruct 有一个替代operator delete,所以你的示例应该是具有预期语义的良好定义。

[expr.delete]/2 状态:

delete 的操作数的值可能是……指向由前一个 new-expression 创建的非数组对象的指针。

Placement new 是一种new-expression[expr.new]/1:

新表达式:
::opt new new-placementopt new-type-id new-initializer选择
::opt new new-placementopt (type-id ) new-initializeropt

delete 被定义为调用对象的析构函数,然后调用内存的释放函数。 [expr.delete]/6,7:

... delete-expression 将为对象调用析构函数(如果有)...

... delete-expression 将调用释放函数...

只要释放函数匹配分配函数(它应该匹配,只要你没有为你的类重载operator delete),那么这一切都应该被很好地定义。

【讨论】:

  • 嗯。您还必须注意,该实现不允许在operator new 的返回值之后添加填充。 (不是;C++11 §5.3.4/10。)即便如此,我仍然对此感到不安。似乎没有不可避免的用例,因为即使您不知道动态类型,dynamic_cast&lt;void*&gt;() 也会为您提供派生最多的指针。
  • The uniform consensus is that passing the return value of placement new to delete is NOT ok 毕竟,placement new 并不要求首先从::operator new() 获得内存。这个答案的其余部分立即崩溃,消除了基本的误解。
【解决方案2】:

[Revised] 一般不行,一般只能delete用匹配的普通new表达式得到的东西。否则,由您来保证释放函数与分配函数匹配(因此使用::delete p; 将是更安全的通用解决方案,假设您原来的operator new 在全局命名空间中)。特别是当相关类(或其派生类之一)重载 operator new 时,您必须小心。

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

p->~MyStruct();

operator delete(buffer);

【讨论】:

  • 除了下意识的“这很奇怪,和记忆有关,所以一定是UB”,你有什么证据?
  • @Mankarse: delete p 可能会调用MyStruct::operator delete,这可能会做一些完全不恰当的事情,而实际上需要::operator delete
  • 是的,但这并不意味着您可以 delete 使用普通的new 表达式获得的东西。它也可以在您知道MyStruct::operator delete 不存在的情况下工作。 (虽然我同意这使得使用 delete 有点不安全,并且在大多数情况下可能是个坏主意)。
  • @Mankarse: Also 5.3.5/2: "delete 的操作数的值可能是 [...] 指向由先前的新表达式创建的非数组对象的指针[...]。如果不是,则行为未定义。”
  • @Mehrdad:但请注意,这种设计并不安全:稍后通过在类中添加一些内容,有人可以远程破坏您的代码,而您永远不会知道。只要做正确的事,你就可以省去所有这些麻烦。 (实际正确的做法是永远不要在客户端代码中自己拼写“新”,而是使用诸如分配器和allocate_shared等的东西。)
【解决方案3】:

不,您通常不应该调用 delete(在某些情况下,例如当 operator delete 被覆盖时,它可能是可以的)。

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 2011-05-24
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 2020-07-18
    • 2011-04-07
    相关资源
    最近更新 更多