【问题标题】:placement delete inside or outside destructor在析构函数内部或外部放置删除
【发布时间】:2014-11-16 16:32:08
【问题描述】:

我在阅读placement new operator时发现了以下代码。

#include <iostream>
using namespace std;

class MyClass {

public:
  // Placement new operator
  void* operator new (size_t sz, void* v) {
    cout << "Placement new invoked" << endl;
    return v;
  }

  ~MyClass() {
    // Cleanup
  }
};

int main()
{
    // Create a buffer to store the object
    int buffer[16];
    cout << "Starting address of my buffer = " << &buffer << endl;

    // Create the object. Use placement new
    MyClass* obj = new (buffer) MyClass();
    cout << "Location of my object = " << obj << endl;

    // Don't delete object created with placement delete
    // Call the destructor explicitly
    obj->~MyClass();
}

我有几个关于删除使用placement new 创建的对象的问题:

  1. 什么是清理代码需要写在析构函数中才能 缓冲内存中被 obj 占用的空闲内存。
  2. 是否不需要定义放置删除,如果是,是否需要在析构函数内部或析构函数外部。如果它在析构函数之外,它将如何被调用?

【问题讨论】:

  • 你只需要显式调用析构函数。没有放置删除。 (但请注意,如果分配了底层缓冲区,则可能需要删除..)
  • 阅读贴出代码末尾的两行注释
  • 没有放置删除之类的东西
  • @CashCow 有,但这里不相关。

标签: c++


【解决方案1】:

为了释放缓冲内存中占用obj的内存,需要在析构函数中写入什么清理代码

析构函数应该做任何析构函数所做的事情:清理对象管理的所有资源。在这种情况下,它不管理任何资源,因此无需执行任何操作。

根据对象本身的存储分配方式,它不应该做任何特殊的事情。如果需要,管理该存储是自定义 newdelete 操作员的工作。

是否不需要定义放置删除

没有。 Placement-new 用于在您自己管理的存储中构造一个对象,您有责任自己释放该存储。在这种情况下,存储是自动的,因此在函数退出时会自动释放。

正如placement-new 的分配器什么都不做(只是将提供的指针返回到预分配的存储),所以相应的释放器什么也不做;所以它不存在。您只需要在自己处理存储之前直接调用其析构函数来销毁该对象。

【讨论】:

  • §18.6.1.3 定义了一个全局的operator delete( void*, void* );如果他定义了一个特定的类operator new( size_t, void* ),那么定义相应的operator delete 可能也是一种很好的形式,以防构造函数抛出。如果不出意外,它将确保对指向对象的指针的删除不会找到全局operator delete(这不太可能与他的位置operator new一起工作)。
  • @JamesKanze,对于上面的代码,能否请您定义运算符删除。如果我调用obj的delete,没有定义operator delete,会有什么影响?
  • @Mike,考虑上面带有整数私有数据成员的示例,然后 MyClass* obj = new (buffer) MyClass();将在缓冲区中为 obj 分配 4 字节的内存。现在你可以解释如何从缓冲区中删除 obj 内存,以便缓冲区保持其旧状态。
  • @user3665615:我不明白你的意思。 Placement-new 不会从缓冲区分配内存(无论您认为这意味着什么);它将使用(部分)缓冲区的内存作为它创建的对象的存储空间。没有对应的删除;在释放或重用缓冲区之前,您必须销毁对象(通过调用其析构函数)。 Placement-new 的全部意义在于它不分配内存。它允许您在自己管理的存储中创建和销毁对象。如果您不想自己管理存储,请不要使用placement-new。
  • @MikeSeymour,感谢您的回复。我的疑问是如何为具有 int 成员(不是指针)的类编写析构函数,以便我可以重用缓冲区。
【解决方案2】:

普通的new 做了两件事:

  1. 为您的对象分配内存
  2. 在该内存空间中构造您的对象。

展示位置new 表示您管理其中一个,而另一个像以前一样管理。

  1. 您为对象分配/提供内存
  2. 在该内存空间中调用构造函数。

相反的是delete,其中常规的delete 执行以下操作:

  1. 调用要清理的对象的析构函数
  2. 释放为其分配的内存。

请注意,出于显而易见的原因,它们以相反的顺序完成。在您完成使用该内存之前,您无法释放包含有关需要清理的信息的内存。而在施工中,您需要先掌握内存。

在所谓的placement delete 中,实际上是placement new 的反面,您需要执行销毁的第一步而不是第二步。因此,您调用对象的析构函数,然后您可以释放它使用的内存/将其用于其他用途。

使用placement new 的最常见示例是在std::vector 的实现中,它需要一个连续的缓冲区来存储其数据,并且可以让您提前保留(如果您不这样做,它可能会为您完成) .该部分分配内存但不构造其中的对象。因此,当它们稍后构建时,将使用placement new。

【讨论】:

  • 实际上,placement new 可能意味着两种不同的东西。在一般意义上,这意味着您可以将附加参数传递给operator new 函数;类型及其含义取决于您。该术语也可用于放置 new 的一种特定变体,其中有一个额外的 void* 参数,它只是返回。
【解决方案3】:

首先要问的是:你想做什么?如果你 在一个类中定义一个放置操作符 new,那么就是 只有在你写 new MyClass 时才会找到的 operator new;你必须总是指定额外的参数。而在 几乎所有定义类特定operator new 的情况,您还应该定义类特定operator delete; 否则将调用全局 operator delete 函数 当你写delete p时,这通常是行不通的。

如果您的目标是系统地要求将 分配和初始化,这就是你定义的原因 一个成员操作符new,那么你可以提供一个无操作operator delete;如果类的构造函数可以抛出,那么你会 还想提供一个放置操作符删除,因为这是 如果新对象的构造函数退出,将调用什么 通过异常。但是,没有其他方法可以调用它。 提供安置操作员新时,您必须提供 一个默认的操作符 delete 做正确的事;什么时候 为同一类型提供多个 operator new,您需要 在每个分配中以某种方式记住调用了哪个分配,在 订单在非放置操作符delete中调度。

顺便说一下,只是将缓冲区分配为局部变量 保证除了 声明的缓冲区类型。

编辑:

只是operator delete 函数(必须是成员)所需的示例:

void operator delete( void* p ) {}
void operator delete( void* p, void* ) {}

【讨论】:

    猜你喜欢
    • 2014-12-27
    • 2014-01-24
    • 1970-01-01
    • 2012-12-22
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    相关资源
    最近更新 更多