放置新的和删除

时间:2011-07-21 23:17:56

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

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

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

  delete (char*)buf;

OR

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

  delete buf;

或两者都相同?

3 个答案:

答案 0 :(得分:46)

正确的方法是:

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

您只能使用delete 运算符删除从new 运算符收到的内容。如果直接调用operator new函数,则还必须直接调用operator delete函数,并且还必须手动调用析构函数。

答案 1 :(得分:24)

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

  1. 新/删除运算符

  2. 新建/删除表达式

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

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

    • 重载的新表达式调用重载的新运算符来分配内存,然后继续构造对象。
    • 但是,没有重载的删除表达式,特别是没有“placement-delete”:相反,你必须手动调用析构函数。

    新的/删除运算符仍然必须在匹配对中重载,因为当对象构造函数抛出异常时会调用匹配的删除运算符。但是,没有自动方法为已经使用重载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
    

答案 2 :(得分:2)

假设没有Buffer::operator delete这样的东西,delete buf;版本是正确的,并将进行所有适当的清理。为了更安全一点,您可以说::delete buf;

语言 - 律师辩论材料如下。

5.3.5 / 1

  

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

     

删除表达式:

     
      
  • :: opt delete cast-expression
  •   
  • :: opt delete [ ] cast-expression
  •   
     

第一种方法是非数组对象,第二种方法是数组。 ...

5.3.5 / 2

  

...在第一个备选方案(删除对象)中,delete的操作数值可能是空指针,指向由非创建的非数组对象的指针前面的 new-expression ,或指向表示此类对象的基类的子对象(1.8)的指针(第10条)。如果不是,则行为未定义。

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

5.3.4 / 1

  

new表达式:

     
      
  • :: opt new new-placement opt < / sub> new-type-id _new-initializer_ opt
  •   
  • :: opt new new-placement opt < / sub> ( type-id ) new-initializer opt
  •   
     

新放置:

     
      
  • ( 表达式列表 )
  •   

所以“placement new”确实算作 new-expression 。没有什么可以禁止 delete-expression

此外,事实证明,无论自定义创建, delete-expression 都能正确清理对象。

5.3.5 / 6-9

  

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

     

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

     

delete-expression 中的关键字delete前面有一元::运算符时,全局释放函数用于释放存储空间。

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

try {
    buf->~Buffer();
} catch(...) {
    ::operator delete(mem);
    throw;
}