为什么要对数组进行特殊的新建和删除?

问题描述 投票:11回答:7

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

在分配和释放数组的幕后有什么特别的事情吗?

为什么与malloc和免费的不同?

c++ c arrays memory-management new-operator
7个回答
14
投票

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

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

使用删除将不会调用正确的功能来删除内存。它应调用delete[](void*),但应调用delete(void*)。因此,您不能依靠delete来分配new[]

分配的内存

See this C++ FAQ

[16.13]我可以在以下情况下放下[]删除一些内置类型的数组(字符,整数等)?

否!

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

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

但是上面的代码是错误的,它可能在运行时造成灾难。在特别是所要求的代码delete poperator delete(void*),但是需要的代码delete[] poperator delete[](void*)。默认行为因为后者叫前者,但允许用户更换后者具有不同的行为(在在这种情况下,他们通常也会替换中的相应新代码运算符new[](size_t))。如果他们替换了delete[]代码,因此与删除不兼容代码,你打错了(即,如果您说delete p而不是比delete[] p),您可能会结束在运行时发生灾难。

为什么delete[]首先存在?

是否执行x或y:

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

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

我认为deletedelete[]决策的原因以及一长串有利于C ++效率的决策清单。这样一来,无需强制执行价格即可查询正常删除操作需要删除的数量。

具有2 newnew[]似乎对于逻辑上来说无论如何都具有deletedelete[]似乎是合乎逻辑的。


12
投票

区别在于delete仅删除整个内存范围,而仅调用1个对象的析构函数。 delete[]将删除内存并为每个对象调用析构函数。如果您不对数组使用delete[],则在应用程序中引入资源泄漏只是时间问题。

编辑更新

根据标准,未定义将分配有new[]的对象传递到deletelikely行为是它会像我描述的那样起作用。


5
投票

Stroustrup在10.3至10.5.1节的“ C ++的设计和演变”中讨论了分别将new / new[]delete/ delete []`运算符分开的原因:

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

这个决定是否是一个错误还是值得商-的-任一种方式都有很好的论据,但是我们拥有我们所拥有的。


4
投票

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

考虑此代码

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

这些都返回一个Foo*指针,不同之处在于第二个调用将导致Foo构造函数被调用10x,并且大约有10x的内存。

所以现在您要释放对象。

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

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

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

真正发生的是,对于new type [size],编译器将'size'秘密存储在其他位置。当您调用delete []时,它将知道此秘密值存在,因此它可以找出该内存块中有多少个对象并将其销毁。

然后您可能会问的问题是“为什么编译器不总是存储大小?”

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

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

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

背后的原因是人们希望人们提供返回动态分配的内存的库,而这些库的用户将无法知道是否使用自由/删除。

这种对兼容性的渴望意味着数组的大小不能存储为数组本身的一部分,而必须保留在其他位置。由于这种开销(请记住,这可以追溯到80年代初),因此决定只保留数组而不是单个元素来编写这本书。因此,数组需要一种特殊的删除语法来查找该值。

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


1
投票

标题中的“为什么”:C ++的设计目标之一是不会有任何隐藏成本。在内存的每个字节仍然比今天重要得多的时候,C ++也被开发出来了。语言设计人员也喜欢正交性:如果使用new[](而不是new)分配内存,则应该使用delete[]释放它。

我不认为有任何[[technical原因,new[]不能在delete的存储块标题中粘贴“我是数组”标志(不再有delete[])待会儿再看。


1
投票
newdeletemallocfree的不同之处在于mallocfree仅分配和释放内存;他们不称呼ctor或dtor。

0
投票
[当您使用new[]分配数组时,实际上是在告诉C ++该数组的大小。当您使用malloc时,您要告诉它分配了多少内存。在前一种情况下,基于数组的大小进行释放是没有意义的。在这种情况下,确实如此。但是,由于数组的指针与单个对象之间没有区别,因此需要单独的函数。
© www.soinside.com 2019 - 2024. All rights reserved.