C++中的delete如何知道要删除多少个内存位置?

7

删除操作符是如何工作的?它比free()更好吗?另外,如果您使用ptr=new char[10],然后使用delete ptr进行删除,指针如何知道要删除多少个位置。


4
我认为你的意思是“删除”运算符。 - SLaks
12
你需要删除 [] ptr,否则就会遇到麻烦。 - AndersK
我冒昧修改了您的标题,希望您不介意。如果您想了解特定编译器和平台的情况,最好将其包含在内。不同的编译器可能有不同的解决问题的方法。 - Skurmedel
g++在Fedora上,同时这个标题更好 - shreyasva
1
请参考以下链接:http://stackoverflow.com/questions/1616937/how-does-delete-know-how-many-elements-to-destroy-closed 和 https://dev59.com/4HVC5IYBdhLWcg3wvT1a。 - Michael Myers
7个回答

18

只有delete运算符,free只存在于函数中。在C++中,建议使用new/delete代替malloc()/free()


delete操作符中有一个内部的“魔法”。当使用new[]创建数组时,数组的大小会存储在内存块的元数据中。delete[]利用这些信息。

当然,所有这些都取决于编译器、操作系统、优化器和实现方式。


我建议将 new 改为 new[],将 delete 改为 delete[] - fredoverflow
4
如果您错误地使用了delete ptr而不是delete [] ptr,那么 "magic" 可能会正常工作,也可能不正常工作,这取决于具体实现。 - Mark Ransom
3
我永远无法理解为什么C++会在内部传递数组大小,但不给你访问它们的权限,迫使你自己传递数组大小。 - BlueRaja - Danny Pflughoeft
2
@BlueRaja: 如果您创建一个长度为n个字节的数组,C++不一定要为该数组精确分配n个字节 - 例如,它可以分配更多的空间,以确保对齐。 - Michael Williamson
@BlueRaja: 因为C++不需要知道数组的大小。具体实现可能知道数组以字节为单位的大小,但不知道最大的数组索引(因为它不关心项的大小)。另一方面,如果您还需要知道大小,也许您需要使用std::vector而不是数组? - Vlad

5

如果您使用的是非数组类型:

T t = new T;
// ...
delete t;

它会知道要删除多少,因为它知道一个 T 有多大。如果你做一个数组:
T t* = new T[10];
// ...
delete [] t;

它将在分配时提供一些额外的信息来告诉它需要删除多少内存。请注意,我们使用delete [] t,其中有额外的[],以告诉它这是一个数组。如果您不这样做,它会认为它只是一个T,并且仅释放那么多内存。

如果您使用new,则始终使用delete,如果您使用malloc,则使用freenew首先分配内存,然后构造对象。类似地,delete调用析构函数,然后释放内存。malloc\free仅处理内存分配和释放。


3
  1. C++中,使用new关键字调用构造函数,但使用malloc时不会调用。同样地,使用delete删除时会调用析构函数,但使用free时不会调用。

  2. 如果你使用MyClass* x = new MyClass[25]创建了一个数组,那么你需要调用delete[] x,这样系统才知道你正在删除(并析构)一组对象而不是单个对象。


3

实现会为您计算需要销毁的元素数量(使用delete[])。例如:

  • new char[10] 可能会分配一些额外的字节,将元素数量放在开头,然后返回第一个元素后的指针。delete[] 然后可以查找指针后面存储的数量。
  • 存储类似于std::map<void*, size_t> 的等效内容,new T[n] 将创建具有返回指针的键和计数的值。delete[] 将在此映射中查找指针
  • 完全不同的东西。重要的是它已经完成了(正如其他答案指出的那样,请使用delete[]与new[] 一起使用!)

3
这并不是好与坏的问题。两者都是为了分配和释放内存而进行的操作,但通常在C++与C的不同上下文中使用。此外,这两组操作之间存在一些重要的差异:
- `new` / `delete` 是C++关键字,不可用于C语言。 - `new` / `delete` 是类型安全的,而 `malloc()` / `free()` 不是。这意味着由 `malloc()` 和 `free()` 返回的指针是需要转换为正确类型的 `void` 指针。 - `new` / `delete` 隐式支持使用编译器提供的元数据使用 `new[]` 和 `delete[]` 语法进行数组分配和删除,而 `malloc()` / `free()` 不支持此语法。 - 对类类型 `T` 应用 `new` / `delete` 将分别调用 `T` 的构造函数和析构函数,而 `malloc()` / `free()` 不会调用 `T` 的构造函数和析构函数。 - `malloc()` 在内存耗尽的情况下将返回 `NULL`,而 `new` 将抛出异常。
请注意,为了防止内存损坏,使用 `new` 分配的内存应该使用 `delete` 进行释放。对于使用 `malloc()` 分配的内存也同样适用,应使用 `free()` 进行释放。

3
具体回答你的问题,delete操作符调用了析构函数,而free没有;这就是为什么你不想混合使用malloc/free和new/delete的原因。
当编译器的库从堆中获取内存时,它会获取比你所要求的稍微多一点的内存,其中包括其自己的管理空间、一些元数据和如果需要的话填充空间。
假设你malloc了128字节,或者new了一个包含8个16字节大小对象的数组。编译器实际上可能会获取256字节,包括一个小的堆管理块在开头,记录它给了你128字节但获取了256字节,并向你返回256块中某个位置的偏移量。你保证从该指针处有128字节可用,但如果超出这个范围,可能会遇到麻烦。
在底层,free和delete操作符将取回你得到的指针,备份已知的偏移量,读取它的头块,并知道释放256字节回到堆中。

2

补充 @Vlad 的回答,如果您使用 new[] 运算符的形式分配内存,例如:

char *c = new char[10];

那么你需要使用相应的删除表单:

delete[] c;

您必须明确告诉编译器在释放内存时使用元数据。

[编辑]

为什么不能使用char c[]

因为编译器需要在编译时知道变量c所需的大小。然而,使用new[]形式告诉它推迟分配直至运行时。因此,出现编译器错误。


char c[] = new char[10]; 无法编译,你应该将 c[] 改为 *c - fredoverflow
那个语句不合法,你不能更改字符数组的指针。我想你可能是想说 char *c。 - shreyasva
即使你删掉了c,我敢打赌它也能工作。不要问我为什么 :) - Ramadheer Singh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接