64

删除此处分配的所有内存的正确方法是什么?

  const char* charString = "Hello, World";
  void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
  Buffer* buf = new(mem) Buffer(strlen(charString));

  delete (char*)buf;

或者

  const char* charString = "Hello, World";
  void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
  Buffer* buf = new(mem) Buffer(strlen(charString));

  delete buf;

还是它们都一样?

4

3 回答 3

65

正确的方法是:

buf->~Buffer();
::operator delete(mem);

您只能通过delete 操作员删除您从new 操作员那里收到的内容。如果直接调用operator new函数,也必须直接调用operator delete函数,并且必须手动调用析构函数。

于 2011-07-21T23:24:12.490 回答
38

C++中有两个独立的概念:

  1. 新/删除运算符

  2. 新/删除表达式

运算符分配和释放内存。new表达式构造对象。delete表达式有时会破坏对象并调用运算符。

为什么是“有时”?因为它取决于表达。裸机,全局new先调用operator-new分配内存,然后构造对象;全局delete调用析构函数并释放内存。new但是和的所有其他重载delete都不同:

  • 重载的 new表达式调用重载的 new运算符来分配内存,然后继续构造对象。
  • 但是,没有重载删除表达式之类的东西,特别是没有“放置删除”:相反,您必须手动调用析构函数。

新/删除操作符仍然必须在匹配对中重载,因为当对象构造函数抛出异常时会调用匹配的删除操作符。但是,没有自动方法为已分配重载new运算符的对象调用析构函数,因此您必须自己执行此操作。

作为第一个也是最基本的例子,考虑placement-new运算符,它被强制采用形式 void * operator new (size_t, void * p) throw() { return p; }。因此,匹配delete运算符被要求什么都不做:void operator delete (void *, void *) throw() { }. 用法:

void * p = ::operator new(5); // allocate only!
T * q = new (p) T();          // construct
q->~T();                      // deconstruct: YOUR responsibility
// delete (p) q;   <-- does not exist!! It would invoke the following line:
::operator delete(p, q);      // does nothing!
::operator delete(q);         // deallocate
于 2011-07-21T23:38:26.967 回答
3

假设没有Buffer::operator deletedelete buf;版本是正确的,并且会进行所有适当的清理。为了更安全一点,你可以说::delete buf;.

语言律师辩论材料如下。

5.3.5/1

delete-expression运算符销毁由new-expression创建的最派生对象 (1.8) 或数组。

删除表达式:

  • ::选择 delete 强制转换表达式
  • ::选择 delete [ ] 强制转换表达式

第一种选择用于非数组对象,第二种选择用于数组。...

5.3.5/2

...在第一种选择(delete object)中,操作数的值可以是空指针,指向由先前的new-expressiondelete创建的非数组对象的指针,或指向表示的子对象(1.8)的指针这种对象的基类(第 10 条)。如果不是,则行为未定义。

因此,指针必须指向由new-expression创建的对象,该对象定义为:

5.3.4/1

新表达式:

  • ::opt new new-placement opt new-type-id new-initializer opt
  • ::opt new new-placement opt ( type-id ) new-initializer opt

新安置:

  • ( 表达式列表 )

因此,“新位置”确实算作new-expression。没有什么禁止删除表达式的。

此外,尽管有自定义创建,但事实证明delete-expression确实正确地清理了对象。

5.3.5/6-9

如果delete-expression的操作数的值不是空指针值,则delete-expression将为要删除的对象或数组的元素调用析构函数(如果有)。...

如果delete-expression的操作数的值不是空指针值,则delete-expression将调用释放函数(3.7.4.2)。否则,未指定是否将调用释放函数。[注意:无论对象的析构函数或数组的某些元素是否抛出异常,都会调用释放函数。-结束注]

删除表达式中的关键字前面delete带有一元运算符时,全局释放函数用于释放存储空间。::

所以::delete buf;完全等价于:

try {
    buf->~Buffer();
} catch(...) {
    ::operator delete(mem);
    throw;
}
::operator delete(mem);
于 2013-06-02T23:59:06.067 回答