operator new()和operator new []()之间的区别?

时间:2010-12-11 17:23:25

标签: c++ memory

fncs:operator new和operator new [](NOT new和new []运算符)之间有什么区别吗?当然除了调用语法?我问,因为我可以使用:: operator new(sizeof(T)* numberOfObject)为我的objs分配X个字节,然后用数组表示法访问它们,那么:: operator new []有什么大不了的。它只是语法糖吗?

#include <new>
#include <iostream>
#include <malloc.h>

using namespace std;
struct X
{
  int data_;
  X(int v):data_(v){}
};
int _tmain(int argc, _TCHAR* argv[])
{
  unsigned no = 10;
  void* vp = ::operator new(sizeof(X) * no);
  cout << "Mem reserved: " << _msize(vp) << '\n';
  X* xp = static_cast<X*>(vp);
  for (unsigned i = 0; i < no; ++i)
  {
    new (xp + i) X(i);
  }
  for (unsigned i = 0; i < no; ++i)
  {
    cout << (xp[i]).data_ << '\n';
  }
  for (unsigned i = 0; i < no; ++i)
  {
    (xp + i)->~X();
  }
  ::operator delete(vp);
  return 0;
}

5 个答案:

答案 0 :(得分:7)

这些函数(operator new等)通常不是要明确调用,而是由new / new[]表达式隐式使用(对称,operator delete / { {1}}函数由operator delete[] / delete表达式隐式调用。对非数组类型使用delete[]语法的表达式将隐式调用new函数,而带operator new的表达式将隐式调用new[]

这里重要的细节是operator new[]表达式创建的数组通常会被new[]表达式稍后销毁。后者将需要知道要销毁的对象的数量(如果对象具有非平凡的析构函数),即该信息必须以某种方式从delete[]表达式(当它已知时)传递到相应的{{1表达式(当需要时)。在典型的实现中,此信息存储在由new[]表达式分配的块内,这就是为什么在delete[]的隐式调用中请求的内存大小通常更大而不是产品元素数量和元素大小。额外空间用于存储家庭信息(元素数量,即)。稍后new[]表达式将检索该家庭信息,并在通过调用operator new[]实际释放内存之前使用它来调用正确数量的析构函数。

在您的示例中,您没有使用任何这些机制。在您的示例中,您正在显式调用内存分配函数,手动执行构造并完全忽略销毁步骤(这是正常的,因为您的对象具有简单的析构函数),这意味着至少为了破坏目的,您不需要跟踪确切的数组中的元素数量。在任何情况下,您都可以在delete[]变量中手动跟踪该号码。

但是,一般情况下是不可能的。在一般情况下,代码将使用operator delete[]表达式和no表达式,并且元素的数量必须以某种方式从new[]delete[],这意味着它必须存储在内部,这就是为什么需要为数组提供专用的内存分配函数 - new[]。它与上述产品的大小仅仅delete[]相当。

答案 1 :(得分:1)

在您的示例代码中,您使用展示位置new来执行operator new[]自动执行的构造 - 区别在于new[]将仅执行默认构建并且您正在执行非默认的展示位置结构。

以下内容或多或少等同于您的示例:

#include <iostream>

using namespace std;

struct X
{
    int data_;
    X(int v=0):data_(v){}
};

int main(int argc, char* argv[])
{
    unsigned no = 10;

    X* xp = new X[no];

    for (unsigned i = 0; i < no; ++i) {
        X tmp(i);
        xp[i] = tmp;
    }

    for (unsigned i = 0; i < no; ++i)
    {
        cout << (xp[i]).data_ << '\n';
    }

    delete[] xp;

    return 0;
}

此示例中的差异是:

  • 我相信这里的示例更具可读性(强制转换可能很难看,而展示位置new是一种非常先进的技术,并不经常使用,因此通常无法理解)
  • 它正确地破坏了分配的对象(在示例中没关系,因为对象是POD,但在一般情况下,你需要为数组中的每个对象调用dtor)
  • 它必须默认构造对象数组,然后迭代它们以设置对象的实际值 - 这个是本例中的一个缺点而不是你的

我认为通常情况下,使用new[] / delete[]比分配原始内存并使用placement new构建对象要好得多。它将簿记的复杂性推向了那些操作员而不是在你的代码中。但是,如果发现“默认构造/设定期望值”操作对的成本太高,那么手动执行它的复杂性可能是值得的。这应该是一种非常罕见的情况。

当然,对new[] / delete[]的任何讨论都需要提及使用new[] /'delete [] should probably be avoided in favor of using std :: vector`。

答案 2 :(得分:0)

函数void* operator new(size_t)void* operator new[](size_t)的必需行为之间没有太大区别,只是它们与不同的释放函数配对。

运营商本身非常不同。运算符之间的差异之一是使用了哪个分配函数,但最终还有许多其他差异,包括调用了多少个构造函数等。但是您的示例代码不使用运算符(好吧,它使用的是placement new)。您可能想要更改问题标题以明确这一点。

来自[basic.stc.dynamic.deallocation]部分:

  

如果解除分配函数终止   通过抛出异常,行为   未定义。第一个的价值   提供给解除分配的参数   function可以是空指针值;   如果是的话,如果解除分配   功能是一个提供的   标准库,呼叫没有   影响。否则,价值   提供给operator delete(void*) in   标准库应为其中之一   前一个返回的值   调用任一运算符   new(std::size_t)或运营商   标准库中的new(std::size_t, const std::nothrow_- t&)和   在标准中提供给operator delete[](void*)的值   库应该是其中一个值   由之前的调用返回   operator new[](std::size_t)或。{   标准中operator new[](std::size_t, const std::nothrow_t&)   库。

答案 3 :(得分:0)

分配是一回事,对象构建/破坏是另一回事。

new执行一次分配和一次构造。但是,new[]仍然会分配一个连续的内存块,但会调用许多构造函数。

情况与deletedelete[]相同。

BTW-我不是100%肯定我要说的,但我相信如果你从{{1}收到的地址上拨打delete,你就不会得到一个内存泄漏}}。整个内存块可能会被释放。但是,这是无效的,因为您只在第一个对象上调用析构函数,而不是在数组中的每个对象上调用析构函数。这可能会导致二次内存泄漏......当然,由于构造函数和析构函数的1-1关系破坏了很多逻辑错误。

(另请注意,请考虑使用new[]boost::array代替std::vector!:))

答案 4 :(得分:0)

正如user sankozhis answerin fact the same question中解释的那样,单独的operator new[]旨在分别在类中重载单个对象分配和数组分配。

例如,如果你有一些特定的类,并且你知道它的实例永远不会超过50个字节,那么你可能希望为该类重载operator new,以便它的实例在块的超高速分配器上分配大小为50。

但是如果用户拨打new[]怎么办?该数组可以包含任意数量的元素,因此您无法在自定义分配器上普遍分配它们。解决方案是您不必关心数组分配,除非您愿意。