11

使用delete而不是有什么问题delete[]

在分配和释放数组的背后是否发生了一些特别的事情?

为什么它会不同于malloc和免费?

4

7 回答 7

15

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

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

使用 delete 不会调用正确的函数来删除内存。它应该调用delete[](void*),但它调用delete(void*). 出于这个原因,您不能依赖使用delete分配的内存new[]

请参阅此 C++ 常见问题解答

[16.13] 删除[]某些内置类型(char、int 等)的数组时可以删除吗?

不!

有时程序员认为 []indelete[] p唯一存在,因此编译器将为数组中的所有元素调用适当的析构函数。由于这个原因,他们假设一些内置类型的数组,例如charorint可以在 delete没有[]. 例如,他们假设以下是有效代码:

void userCode(int n)  {
    char* p = new char[n];
    ...
    delete p; // ← ERROR! Should be delete[] p !
}

但是上面的代码是错误的,它会在运行时造成灾难。特别是,被调用的 代码delete poperator delete(void*),但被调用的代码 delete[] poperator delete[](void*)。后者的默认行为是调用前者,但允许用户用不同的行为替换后者(在这种情况下,他们通常也会替换 operator 中相应的新代码new[](size_t))。如果他们替换了delete[]代码,使其与删除代码不兼容,并且您调用了错误的代码(即,如果您说delete p而不是delete[] p),那么您可能会在运行时遇到灾难。

为什么delete[]首先存在?

无论你做 x 还是 y:

 char * x = new char[100]; 
 char * y = new char;

两者都存储在char *类型变量中。

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

拥有 2new并且new[]似乎只有逻辑才能拥有deletedelete[]无论如何都是对称的。

于 2009-03-18T17:21:43.133 回答
12

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

编辑更新

根据标准,传递分配给new[]to的对象delete是未定义的。可能的行为是它会像我描述的那样运行。

于 2009-03-18T17:22:11.433 回答
6

Stroustrup在第 10.3 到 10.5.1 节的“C++ 的设计和演变”中谈到了分离new/new[]和delete[]` 运算符的原因:delete/

  • 10.3 数组分配- 讨论他们想要一种方法来允许使用与分配单个对象不同的方案来分配对象数组(即,从单独的存储中分配数组)。new添加和的数组版本delete是解决此问题的方法;
  • 10.5.1 解除分配数组- 讨论仅使用单个delete运算符解除分配数组的问题是,需要比指针更多的信息来确定指针是指向数组的第一个元素还是只指向数组的第一个元素到单个对象。delete[]运算符用于处理数组,而不是“使分配和释放单个对象的常见情况复杂化” 。这符合“不为不使用的东西付费”的一般 C++ 设计理念。

这个决定是否是一个错误是有争议的——无论哪种方式都有很好的论据,但我们拥有我们所拥有的。

于 2009-03-18T19:23:13.840 回答
5

这个要求的原因是历史性的,因为new typenew type [size]返回不同的东西需要不同的清理。

考虑这段代码

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

它们都返回一个Foo*指针,不同之处在于第二次调用将导致 Foo 构造函数被调用 10 倍,并且大约有 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没有这个问题是他们只是处理内存块,不必担心调用构造函数/析构函数。

于 2009-03-18T18:09:35.173 回答
1

newdelete不同mallocfreemallocfree只分配和释放内存;他们不叫ctors或dtors。

于 2009-03-18T17:38:02.893 回答
1

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

我认为没有任何技术原因new[]不能在内存块的标题中粘贴“我是一个数组”标志以供delete(不再delete[])查看。

于 2009-03-18T18:26:54.803 回答
0

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

于 2009-03-18T17:26:30.080 回答