为什么有一个特殊的new和删除数组?

时间:2009-03-18 17:19:59

标签: c++ c arrays memory-management new-operator

使用delete代替delete[]有什么问题?

在分配和释放数组的过程中是否有一些特殊的事情发生?

为什么它与malloc和免费?

有所不同

7 个答案:

答案 0 :(得分:14)

使用new[]创建的对象必须使用delete[]。在数组上使用delete未定义。

使用malloc和free,你会有一个更简单的情况。只有一个函数可以释放您分配的数据,也没有任何析构函数被调用的概念。混淆只是因为delete[]和删除看起来相似。实际上它们是两个完全不同的功能。

使用delete不会调用正确的函数来删除内存。它应该调用delete[](void*),而是调用delete(void*)。因此,您不能依赖delete使用new[]

分配的内存

See this C++ FAQ

  

[16.13]我可以放弃[]吗?   删除某些内置类型的数组   (char,int等)?

     

没有!

     

有时程序员会认为   []中的delete[] p仅存在   编译器会调用相应的   对于所有元素的析构函数   阵列。由于这个推理,他们   假设一些内置的数组   charint等类型可以   没有delete的{​​{1}} d。例如,他们   假设以下是有效代码:

[]
     

但上面的代码是错的,而且它   可以在运行时导致灾难。在   特别是,需要的代码   void userCode(int n) { char* p = new char[n]; ... delete p; // ← ERROR! Should be delete[] p ! } delete p,   但是需要的代码   operator delete(void*)delete[] p。默认行为   对于后者是打电话给前者,   但是允许用户更换   后者有不同的行为(in   他们通常也会这样   替换相应的新代码   operator operator delete[](void*))。如果他们   替换了new[](size_t)代码   与删除不兼容   代码,你打错了   (即,如果你说delete[]   比delete p),你可能会结束   在运行时发生灾难。

为什么delete[] p首先存在?

你是做x还是y:

delete[]

两者都存储在 char * x = new char[100]; char * y = new char; 类型的变量中。

我认为char *delete决定的原因伴随着一长串有利于C ++效率的决策。这样就没有强制价格来查找正常删除操作需要删除多少。

拥有2 delete[]new似乎只有new[]delete才对称。

答案 1 :(得分:12)

不同之处在于delete只会删除整个内存范围,但只会为1个对象调用析构函数。 delete[]将删除内存并为每个对象调用析构函数。如果不对数组使用delete[],那么在将资源泄漏引入应用程序之前只是时间问题。

编辑更新

根据标准,将new[]分配给delete的对象传递给{{1}}是不确定的。 可能的行为是它将按照我的描述行事。

答案 2 :(得分:5)

Stroustrup在第10.3至10.5.1节中讨论了“C ++的设计和演变”中单独的new / new[]delete/ delete []`运算符的原因:< / p>

  • 10.3数组分配 - 讨论了他们想要一种方法来允许使用与分配单个对象分开的单独方案来分配对象数组(即,从单独的存储分配数组)。添加newdelete的数组版本是一个解决方案;
  • 10.5.1取消分配数组 - 讨论如何仅使用单个delete运算符解除分配数组的问题是,除了指针之外需要更多信息以确定是否指针指向数组的第一个元素,或者它指向一个对象。 delete[]运算符用于处理数组,而不是“使分配和释放单个对象的常见情况复杂化”。这符合一般的C ++设计哲学“不要为你不使用的东西买单”。

这个决定是否是一个错误是值得商榷的 - 无论哪种方式都有充分的论据,但我们拥有我们所拥有的。

答案 3 :(得分:3)

此要求的原因是历史性的,因为new typenew type [size]会返回需要以不同方式清理的不同内容。

考虑这段代码

Foo* oneEntry = new Foo;
Foo* tenEntries = new Foo[10];

这两个都返回一个Foo *指针,区别在于第二个调用将导致Foo构造函数被调用10x,并且内存大约是内存的10倍。

所以现在你要释放你的物品。

对于单个对象,您可以调用delete - 例如delete oneEntry。这会调用对象析构函数并释放内存。

但问题在于 - oneEntry和tenEntries都只是Foo指针。编译器不知道它们是指向一个,十个还是一千个元素。

使用delete []的特殊语法时。这告诉编译器“这是一个对象数组,计算出计数,然后将它们全部销毁”。

真正发生的是,对于new type [size],编译器在其他地方秘密存储“大小”。当你调用delete []时,它知道这个秘密值存在,因此它可以找出该内存块中有多少个对象并销毁它们。

您可以问的问题是“为什么编译器不会始终存储大小?”

这是一个很好的问题,它可以追溯到C ++的早期阶段。希望对于内置类型(char,int,float等),以下内容对C ++有效;

int* ptr = new int;
free(ptr);

int* ptr = (int*)malloc(sizeof(int) * someSize);
delete ptr;

这背后的原因是期望人们会提供返回动态分配内存的库,而这些库的用户无法知道是否使用free / delete。

这种对兼容性的要求意味着数组的大小不能作为数组本身的一部分存储,而是必须保存在其他地方。由于这种开销(并且记得,这是在早期的80年代),所以决定做这本书只保留数组而不是单个元素。因此,数组需要一种特殊的删除语法来查找该值。

malloc / free没有这个问题的原因是它们只是处理内存块而不必担心调用构造函数/析构函数。

答案 4 :(得分:1)

newdeletemallocfree不同,mallocfree只分配和释放内存;他们不打电话给ctors或dtors。

答案 5 :(得分:1)

关于标题中的“为什么”:C ++的设计目标之一是不存在任何隐藏成本。 C ++也是在内存的每个字节仍然比现在更重要的时候开发的。语言设计者也喜欢正交性:如果你用new[](而不是new)分配内存,你应该用delete[]释放它。

我认为new[]无法在delete内存块的标头中粘贴“我是一个数组”标志的技术原因(稍后不再需要delete[])。

答案 6 :(得分:0)

使用new[]分配数组时,实际上是在告诉C ++数组的大小。当您使用malloc时,您会告诉它分配了多少内存。在前一种情况下,基于数组大小的释放是没有意义的。在这种情况下,确实如此。但由于数组指针与单个对象之间没有区别,因此需要单独的函数。