为什么[]用于删除(删除[])以释放动态分配的数组?

Ash*_*ish 10 c++ arrays

我知道什么时候delete []会导致所有数组元素的破坏,然后释放内存.

我最初认为编译器希望它只是为数组中的所有元素调用析构函数,但我还有一个反对的参数:

堆内存分配器必须知道分配的字节大小,并使用sizeof(Type)它可能找不到元素,并为数组调用适当的no析构函数以防止资源泄漏.

所以我的假设是正确与否,请清楚我对它的怀疑.

所以,我没有得到的使用[]delete []

Gre*_*osz 36

Scott Meyers在他的Effective C++书中说:第5项:在new和delete的相应用途中使用相同的形式.

删除的一个重要问题是:被删除的内存中有多少个对象?答案决定了必须调用多少个析构函数.

删除指针是指向单个对象还是指向对象数组?删除知道的唯一方法是让你告诉它.如果在使用delete时不使用括号,则delete假定指向了单个对象.

此外,内存分配器可能会分配更多存储对象所需的空间,在这种情况下,将每个对象大小返回的内存块大小分开将不起作用.

根据平台,_msize(windows),malloc_usable_size(linux)或malloc_size(osx)函数将告诉您分配的块的实际长度.在设计种植容器时可以利用此信息.

它无法工作的另一个原因是Foo* foo = new Foo[10]调用operator new[]分配内存.然后delete [] foo;调用operator delete[]释放内存.由于这些运算符可能会被重载,因此您必须遵守约定,否则delete foo;调用operator delete可能具有不兼容的实现operator delete [].这是语义问题,而不仅仅是跟踪已分配对象的数量,以便稍后发出正确数量的析构函数调用.

也可以看看:

[16.14]在p = new Fred [n]之后,编译器如何知道在delete [] p期间有n个对象需要被破坏?

简短回答:魔术.

答案很长:运行时系统存储对象的数量n,如果你只知道指针p,它可以在哪里检索它.有两种流行的技术可以做到这一点.这两种技术都被商业级编译器使用,两者都有权衡,而且都不是完美的.这些技术是:


编辑:在阅读了@AndreyT评论之后,我挖掘了Stroustrup的"C++的设计和演变"副本并摘录了以下内容:

我们如何确保正确删除数组?特别是,我们如何确保为数组的所有元素调用析构函数?

...

处理单个对象和数组不需要普通删除.这避免了分配和解除分配单个对象的常见情况的复杂化.它还避免了使用数组释放所需的信息来阻止单个对象.

delete []的中间版本要求程序员指定数组的元素数.

...

这被证明太容易出错,因此跟踪元素数量的负担被放在了实现上.

正如@Marcus所提到的,理性可能是"你不为你不使用的东西买单".


EDIT2:

在"The C++ Programming Language,3rd edition",§10.4.7中,Bjarne Stroustrup写道:

究竟如何分配数组和单个对象是依赖于实现的.因此,不同的实现将对delete和delete []运算符的错误使用做出不同的反应.在像前一个一样简单且无趣的情况下,编译器可以检测到问题,但通常会在运行时发生令人讨厌的事情.

数组的特殊销毁运算符delete []在逻辑上不是必需的.但是,假设已经要求免费存储的实现为每个对象保存足够的信息,以告知它是个人还是数组.用户本可以免除负担,但这种义务会给某些C++实现带来大量的时间和空间开销.

  • @AndreyT:听起来更像是一种不付钱而不是任意使用的决定. (3认同)
  • 斯科特的*解释*根本不是一个解释.这只是一个事实的诅咒,狡猾地作为一个"解释".他说,了解它是数组还是单个对象的唯一方法是询问用户.这当然是不正确的.完全可以将这些信息存储在`new []`中,然后在`delete`中检索,就像现在使用元素计数一样.这个决定是针对它的,因为它会过度复杂的"家庭"信息结构和"删除"中的分支.没有"美丽"的解释,只是*决定*那样. (2认同)

AnT*_*AnT 10

最主要的原因,因此决定保持独立delete,并delete[]为这两个实体并不像类似,因为它可能第一眼看起来.对于一个天真的观察者来说,他们似乎几乎是相同的:只是破坏和解除分配,只有潜在的待处理对象数量的差异.实际上,差异更为显着.

两者之间最重要的区别是delete可能执行对象的多态删除,即所讨论的对象的静态类型可能与其动态类型不同.delete[]另一方面,必须严格处理数组的非多态删除.因此,在这两个实体内部实现了两者之间明显不同且不相交的逻辑.由于多态删除的可能性,其功能delete甚至delete[]与1个元素的数组上的功能远远不同,因为天真的观察者可能最初错误地假设.

与其他一些答案中的奇怪声明相反,当然,完全可以替换delete并且delete[]只使用一个在早期阶段就会分支的构造,即它将决定内存块的类型(数组与否) )使用将由new/ 存储的家庭信息,new[]然后跳转到适当的功能,相当于deletedelete[].然而,这将是一个相当糟糕的设计决策,因为再一次,两者的功能太不同了.将两者强制为单个构造将类似于创建具有释放功能的瑞士军刀.此外,为了能够从非数组中分辨出一个数组,我们必须将一个额外的家庭信息引入到使用plain完成的单个对象内存分配中new.这可能很容易导致单个对象分配中显着的内存开销.

但是,再一次,这里的主要原因是delete和之间的功能差异delete[].这些语言实体仅具有明显的皮肤深度相似性,仅存在于天真规范("破坏和自由记忆")的层面,但是一旦人们逐渐了解这些实体真正需要做什么就会意识到它们太过不同了.合并成一个.

PS这是BTW关于sizeof(type)你在问题中提出的建议的问题之一.由于具有潜在的多态性delete,您不知道其中的类型delete,这就是您无法获得任何类型的原因sizeof(type).这个想法存在更多问题,但已经足以解释为什么它不会飞.