放置新的和删除

Vin*_*ink 47 c++ new-operator visual-c++ 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;
Run Code Online (Sandbox Code Playgroud)

要么

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

  delete buf;
Run Code Online (Sandbox Code Playgroud)

还是他们都一样?

bdo*_*lan 51

正确的方法是:

buf->~Buffer();
::operator delete(mem);
Run Code Online (Sandbox Code Playgroud)

您只能向delete 操作员删除从new 操作员处收到的内容.如果直接调用该operator new函数,还必须直接调用该operator delete函数,并且还必须手动调用析构函数.

  • `delete buf` 是未定义的行为。“删除表达式运算符会破坏由 new 表达式创建的最派生对象 (1.8) 或数组。” 由于 `::operator new` 不是 new 表达式,因此违反了约束,因此未定义行为。 (3认同)
  • 但是`new(mem)Buffer(strlen(charString))`是一个_new-expression_. (2认同)

Ker*_* SB 25

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

  1. 新/删除运算符.

  2. 新/删除表达式.

运算符分配和释放内存.该new表达构建体的物体.的delete表达有时会破坏一个对象,并调用操作者.

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

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

新的/删除操作符仍然必须在匹配对中重载,因为当对象构造函数抛出异常时,将调用匹配的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
Run Code Online (Sandbox Code Playgroud)

  • 您对这两个独立的概念是正确的,但是 `::operator new` 在标准中被称为分配函数,而不是运算符。所以项目符号 #1 应该说“`operator new()` 和 `operator delete()` 分配和释放函数。 (2认同)
  • 你的最后一行不是```::operator delete(p); ``` 因为这是分配内存的地方? (2认同)

asc*_*ler 5

正如其他答案中所解释的,更好、更明智的方法是

buf->~Buffer();
::operator delete(mem);
Run Code Online (Sandbox Code Playgroud)

然而,假设不存在诸如 之类的东西Buffer::operator delete,该delete buf;版本在技术上是没问题的,并且会进行所有相同的清理工作。为了避免这种Buffer::operator delete可能性,你可以说::delete buf;

语言律师辩论材料如下。

5.3.5/1

删除表达式运算符会销毁由new 表达式创建的最派生对象 (1.8) 或数组。

删除表达式:

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

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

5.3.5/2

...在第一个替代方案(delete object)中, 的操作数的值可以是空指针、指向由先前的new 表达式delete创建的非数组对象的指针,或者指向表示的子对象(1.8)的指针此类对象的基类(第 10 条)。如果不是,则行为未定义。

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

5.3.4/1

新表达:

  • ::选择 new 新放置选择 新类型ID 新初始化程序选择
  • ::选择 new 新放置选择 ( 类型ID ) 新初始化程序选择

新位置:

  • ( 表达式列表 )

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

此外,事实证明,尽管是自定义创建的,但删除表达式确实正确地清理了对象。

5.3.5/6-9

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

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

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

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

try {
    buf->~Buffer();
} catch(...) {
    ::operator delete(mem);
    throw;
}
::operator delete(mem);
Run Code Online (Sandbox Code Playgroud)

  • 虽然这在缓冲区已动态分配并且对象的地址与 charString 的地址没有不同的特殊情况下可能有效,但它只是在这里“意外”起作用并且通常是错误的。对任意地址调用 `::operator delete(mem);` 会导致灾难。实现无法知道它来自新的放置。 (2认同)