新建和删除放置

发布于 2024-11-25 16:34:59 字数 478 浏览 1 评论 0原文

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

  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;

它们都是一样的?

What is the right method to delete all the memory allocated here?

  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;

or are they both same?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甜扑 2024-12-02 16:34:59

正确的方法是:

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

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

The correct method is:

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

You can only delete with the delete operator what you received from the new operator. If you directly call the operator new function, you must also directly call the operator delete function, and must manually call the destructor as well.

盗琴音 2024-12-02 16:34:59

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

  1. 新建/删除运算符

  2. 新建/删除表达式

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

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

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

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

作为第一个也是最基本的示例,请考虑放置新运算符,它被强制采用void * operator new (size_t, void * p) throw() { return p; } 的形式。 }。因此,匹配的删除操作符被强制不执行任何操作:void操作符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

There are two separate notions in C++:

  1. The new/delete operators.

  2. New/Delete expressions.

The operators allocate and deallocate memory. The new expression constructs objects. The delete expression sometimes destroys an object and calls the operator.

Why "sometimes"? Because it depends on the expression. The naked, global new first calls operator-new to allocate memory and then constructs the object; the global delete calls the destructor and deallocates the memory. But all other overloads of new and delete are different:

  • An overloaded new expression calls an overloaded new operator to allocate memory and then proceeds to construct the object.
  • However, there is no such thing as an overloaded delete expression, in particular there is no "placement-delete": Instead, you have to call the destructor manually.

New/Delete operators still have to be overloaded in matching pairs, because the matching delete operator is called when an object constructor throws an exception. However, there is no automatic way to invoke the destructor for an object that has been allocated with an overloaded new operator, so you have to do that yourself.

As the first and most basic example, consider the placement-new operator, which is mandated to take the form void * operator new (size_t, void * p) throw() { return p; }. The matching delete operator is thus mandated to do nothing: void operator delete (void *, void *) throw() { }. Usage:

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
心房的律动 2024-12-02 16:34:59

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

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

但是,假设没有 Buffer::operator delete 这样的东西,则 delete buf; 版本在技术上是可以的,并且将进行所有相同的清理。为了避免 Buffer::operator delete 的可能性,您可以说 ::delete buf;

语言律师辩论材料如下。

5.3.5/1

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

删除表达式:

  • ::选择 删除 强制转换表达式
  • ::opt 删除 [ ] 强制转换表达式

第一个替代方案适用于非数组对象,第二个替代方案适用于数组。 ...

5.3.5/2

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

所以指针必须指向一个由new-表达式创建的对象,其定义为:

5.3.4/1

新表达式:

  • ::选择 新放置opt 新类型 ID 新初始化程序opt
  • ::选择 新放置opt ( 类型 ID ) 新初始化程序< em>选择

新展示位置:

  • ( 表达式列表 )

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

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

5.3.5/6-9

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

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

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

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

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

The better and saner way, as explained in other answers, is

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

However, assuming there's no such thing as Buffer::operator delete, the delete buf; version is technically okay and will do all the same cleanup. To avoid the Buffer::operator delete possibility, you can say ::delete buf;.

Language-lawyer debate material follows.

5.3.5/1

The delete-expression operator destroys a most derived object (1.8) or array created by a new-expression.

delete-expression:

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

The first alternative is for non-array objects, and the second is for arrays. ...

5.3.5/2

... In the first alternative (delete object), the value of the operand of delete may be a null pointer, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (1.8) representing a base class of such an object (Clause 10). If not, the behavior is undefined.

So the pointer must point at an object created by a new-expression, which is defined:

5.3.4/1

new-expression:

  • ::opt new new-placementopt new-type-id new-initializeropt
  • ::opt new new-placementopt ( type-id ) new-initializeropt

new-placement:

  • ( expression-list )

So a "placement new" does count as a new-expression. Nothing forbidding a delete-expression there.

Also, it turns out the delete-expression does exactly the right things to clean up the object despite the custom creation.

5.3.5/6-9

If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. ...

If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be called. [Note: The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception. - end note]

When the keyword delete in a delete-expression is preceded by the unary :: operator, the global deallocation function is used to deallocate the storage.

So ::delete buf; is entirely equivalent to:

try {
    buf->~Buffer();
} catch(...) {
    ::operator delete(mem);
    throw;
}
::operator delete(mem);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文