【问题标题】:Is it necessary to overload placement new operator, when we overload new operator?当我们重载 new 运算符时,是否有必要重载placement new 运算符?
【发布时间】:2010-08-30 11:38:27
【问题描述】:

我有以下代码

class Test 
{
public:
    Test(){}
    Test(int i) {}

  void* operator new (size_t size)
  {
      void *p = malloc(size);
      return p;
  }
  //void* operator new (size_t size, Test *p)
  //{
     // return p;
  //}
};

int main() { 
   Test *p = new Test;
   int i = 10;
   new(p) Test(i);
}

以上代码不能在 Visual Studio 中编译,除非我取消注释掉重载的placement new 操作符函数。 如果我注释掉正常重载的新的,在这种情况下它也可以正常工作。 重载普通新运算符时是否必须重载placement new(如果该类需要使用placement new)

放置删除相关代码这里不显示。

【问题讨论】:

    标签: c++


    【解决方案1】:

    通常不会,因为它不经常使用。但这可能是必要的,因为当您在类中重载 operator new 时,它会隐藏全局 ::operator new 的所有重载。

    因此,如果您想在该类的对象上使用放置 new,请执行;否则不要。 nothrow new 也是如此。

    如果您刚刚更改了分配方案,并且您对某处有人在您背后使用新的安置感到惊讶,那么在应用此创可贴之前可能需要进行调查。

    如果该类在标准库容器中使用,而不是直接与new 一起使用,则自定义分配方案应由分配器类定义,而不是重载。默认分配器std::allocator 不尊重成员operator new 重载,而是绕过它们。见下文。


    免责声明:类作用域operator new 重载主要用于调试,即使这样也很难获得可靠的有意义的语义。当心:

    • 您还需要重载operator delete。 (在这个问题的例子中没有做。)

    • 限定语法::new T 将绕过重载。你无法阻止这种绕过。这就是std::allocator<T> 分配事物的方式。你可以为你的类型专门化std::allocator,但这已经是进入兔子洞的某种方式了。

    • 1234563不合格的new 表达式失败。
    • 对于您在班级中采用的每个::operator new,您必须提供具有正确语义的相应成员位置operator delete。如果构造函数因异常退出,则会调用此方法。没有它只会在非常特殊的情况下,在可能资源受限的池中导致内存泄漏。

    总之,成员operator new 是防御性编码的对立面。

    【讨论】:

    • 实际上,出于这个原因,每当您实现自定义new 时,实现放置而不抛出new 是一个好主意。您可能会因未能提供六个版本的 new 和 delete(不要忘记数组形式)而破坏现有代码。
    • placement new 在代码中不常用,但 STL 容器经常使用它来将内存分配与对象构造分开。
    • @Philip:那么这是一个危险信号,因为std::allocator::operator new 获取它的内存。在这种情况下,解决方案是实现自定义分配器,而不是启用标准分配器。
    • @Potato:不,这不是问题。问题是如果std::vector<MyClass> 需要放置new 来构造MyClass,那么MyClass 最好提供一个。 vector 的内存来自哪里并不重要。
    • @Philip:如果vector 从错误的位置获取内存,那就是一个错误。实施自定义分配器也可能需要放置新的(无论如何,按照法律规定),因此它可能会以一种或另一种方式返回。也许我不应该草率​​下结论,但这绝对是个问题。
    【解决方案2】:

    默认情况下,类不存在放置 new 运算符,因此当您调用 new(p) Test(i); C++ 编译器时,在上面的示例中找不到注释函数的定义。如果您取消对类的放置操作符 new 的注释,并注释掉“正常”的操作符,那么将使用默认的“正常”新操作符并且您的代码将被编译。

    【讨论】:

      【解决方案3】:

      std::_Construct 的实现使用 global 放置 new。因此,不必担心 STL 兼容性问题。但是担心破坏现有代码,可能已经写了

      new ((void*)p) thing;

      而不是

      ::new ((void*)p) thing;

      当然是一个有效的观点。

      【讨论】:

        猜你喜欢
        • 2019-02-09
        • 1970-01-01
        • 2012-07-31
        • 1970-01-01
        • 2011-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多