C++ 为什么可以';运行时环境是否决定应用delete或delete[],而不是程序员?
我已经读到需要使用C++ 为什么可以';运行时环境是否决定应用delete或delete[],而不是程序员?,c++,memory-management,dynamic-memory-allocation,C++,Memory Management,Dynamic Memory Allocation,我已经读到需要使用delete[]操作符,因为运行时环境不保留有关分配的块是否是需要析构函数调用的对象数组的信息,但它实际上保留了有关分配的块在内存中存储的位置的信息,当然还有块的大小。 如果在删除时是否需要调用析构函数,只需再记住一点元数据,那么为什么不这样做呢 我很肯定有一个很好的解释,我不是在质疑它,我只是想知道它。 < P>我认为原因是C++不会强迫你做任何你不想要的事情。它会添加额外的元数据,如果有人不使用它,则额外的开销将被强加给他们,这与C++语言的设计目标形成对比。 当你想要你描
delete[]
操作符,因为运行时环境不保留有关分配的块是否是需要析构函数调用的对象数组的信息,但它实际上保留了有关分配的块在内存中存储的位置的信息,当然还有块的大小。如果在删除时是否需要调用析构函数,只需再记住一点元数据,那么为什么不这样做呢
我很肯定有一个很好的解释,我不是在质疑它,我只是想知道它。 < P>我认为原因是C++不会强迫你做任何你不想要的事情。它会添加额外的元数据,如果有人不使用它,则额外的开销将被强加给他们,这与C++语言的设计目标形成对比。
当你想要你描述的能力时,C++确实提供了一种方法。它被称为
std::vector
,您几乎应该总是喜欢它,另一种容器,或者在原始new
和delete
上使用智能指针。C++让您尽可能高效地跟踪一个块中的元素数量,而每个块只需要额外使用4个字节
这可能对很多人有用,但也妨碍了那些不介意投入[]的人的总体效率
<>这与C++和java的区别相似。java可以更快地编程,因为你不必担心垃圾回收,但是C++如果编程正确,因为它不必存储任何这些变量,并且您可以决定何时删除内存块,所以它可以更高效且使用更少的内存。基本上可以归结为语言设计不希望对实现者施加太多限制。许多C++运行时使用
malloc
/free
不提供记录大量元素所需的簿记,也不提供在free
时间确定malloc
'd大小的方法。从C/C++的角度来看,在new Foo
和malloc
之间为每个对象添加另一个级别的内存操作是复杂性/抽象性的一个相当大的飞跃。除此之外,将此开销添加到每个对象会使一些内存管理方法出错,这些方法是在知道对象大小的情况下设计的。这里有两件事需要澄清
第一:假设malloc
保持您要求的精确大小
不是真的malloc
只关心提供足够大的块。尽管出于效率原因,它可能不会过度分配,但它仍然可能会给您一个“标准”大小的块,例如2^n
bytes块。因此,实际大小(如实际分配的对象数量)实际上是未知的
第二:需要“额外位”
事实上,一个给定对象要知道它是否是数组的一部分所需的信息只是一个额外的位。合乎逻辑
不过,就实现而言:您实际会将该位放在哪里
分配给对象本身的内存可能不应该被触摸,毕竟对象正在使用它。那么
- 在某些平台上,这可以保存在指针本身(某些平台忽略部分位),但这是不可移植的
- 所以它需要额外的存储,至少一个字节,除了对齐问题,它可能会达到8个字节
那么正确的答案是什么呢?正确的答案是,恢复类型系统丢失的信息代价高昂。不幸的是。可能。。。尽管有人可能会说C语言中的数组语义已经被破坏了,所以它只是从这里开始。我担心忽略尺寸是过早的微观优化。我认为这不是问题。关于
std::vector
。。。不完全是因为它可以动态增长。一个更简单的容器就行了。尽管如此,还是很接近。你是否在问为什么会有delete[]
而不是delete
来处理数组的解除分配和销毁?这是一个小成本,但支付它的收益也很小。除非标准明确规定分配的大小存储在某个地方(我对此表示怀疑),并不总是需要存储分配的大小。举个例子:int*p=newint
以定义的方式删除此项的唯一方法是在int*
上调用delete。因此,从您知道它是一个int
的事实中可以隐式地知道大小。(注意,在void*
上调用delete是UB:)@trinithis——两个操作符delete
和delete[]
都使用void*
,它们不知道类型。@GeneBushuyev,您混淆了delete操作符(可能是用户提供的)实现的签名和操作符的调用。该标准允许实现采用可选的size参数,该参数设置为删除对象的大小
// A plain array of doubles:
+-------+-------+-------
| 0 | 1 | 2
+-------+-------+-------
// A tentative to stash our extra bit
+-------++-------++-------++
| 0 || 1 || 2 ||
+-------++-------++-------++
// A correction since we introduced alignment issues
// Note: double's aligment is most probably its own size
+-------+-------+-------+-------+-------+-------
| 0 | bit | 1 | bit | 2 | bit
+-------+-------+-------+-------+-------+-------