sha*_*oth 26 c++ memory-management undefined-behavior
首先,根据C++标准,使用delete
分配的任何内容new[]
都是未定义的行为.
在Visual C++ 7中,这种配对可能导致两种后果之一.
如果类型new []'ed有简单的构造函数和析构函数VC++只是使用new
而不是new[]
和使用delete
该块工作正常 - new
只调用"分配内存",delete
只调用"空闲内存".
如果new []'ed类型有一个非平凡的构造函数或析构函数,则无法完成上述技巧 - VC++ 7必须调用恰当数量的析构函数.因此它预先在数组中size_t
存储元素的数量.现在,地址返回的地址new[]
到第一个元素,而不是块的开头.因此,如果delete
使用它只调用第一个元素的析构函数和调用"空闲内存",其地址不同于"分配内存"返回的地址,这导致HeapFree()内部的一些错误指示我怀疑是指堆腐败.
然而,每个人都可以阅读使用delete
after new[]
导致内存泄漏的错误陈述.我怀疑任何大小的堆损坏都比仅为第一个元素调用析构函数并且可能没有调用的析构函数没有释放堆分配的子对象这一事实重要得多.
如何使用delete
之后new[]
可能只会导致某些C++实现中的内存泄漏?
Tho*_*mas 29
假设我是一个C++编译器,并且我实现了这样的内存管理:我将每个预留内存块添加到内存大小(以字节为单位).像这样的东西;
| size | data ... |
^
pointer returned by new and new[]
Run Code Online (Sandbox Code Playgroud)
注意,就内存分配而言,new
和之间没有区别new[]
:两者都只分配一定大小的内存块.
现在将如何delete[]
知道数组的大小,以便调用正确数量的析构函数?简单地将size
内存块除以sizeof(T)
,T
数组元素的类型.
现在假设我delete
只是对析构函数执行一次调用,然后释放size
字节,然后永远不会调用后续元素的析构函数.这导致后续元素分配的资源泄漏.然而,因为我做免费size
字节(而不是sizeof(T)
字节),没有发生堆损坏.
AnT*_*AnT 12
关于混合new[]
和delete
据称导致记忆泄漏的童话故事就是:一个童话故事.它在现实中完全没有立足点.我不知道它来自哪里,但到现在它已经获得了自己的生命,像病毒一样幸存下来,通过口口相传从一个初学者传播到另一个.
这背后"内存泄漏"无义的最可能的理由是,从视天真幼稚点之间的差值delete
,并delete[]
是delete
用来销毁只是一个对象,而delete[]
破坏对象的数组("多"的对象).通常由此得出的一个天真的结论是,数组的第一个元素将被破坏delete
,而其余元素将持续存在,从而产生所谓的"内存泄漏".当然,任何对典型堆实现至少基本了解的程序员都会立即明白最可能的结果是堆损坏,而不是"内存泄漏".
对于天真的"内存泄漏"理论的另一个流行解释是,由于调用了错误数量的析构函数,因此数组中对象所拥有的辅助内存不会被释放.这可能是真的,但这显然是一种非常强制性的解释,在面对更严重的堆损坏问题时几乎没有相关性.
简而言之,混合不同的分配函数是导致可靠,不可预测和非常实际的未定义行为的错误之一.任何试图对这种不确定行为的表现施加一些具体限制的尝试都只是浪费时间和确定缺乏基本理解的迹象.
不必说,new/delete
而new[]/delete[]
事实上是两个独立的内存管理机制,这是独立定制的.一旦他们得到定制(通过替换原始内存管理功能),绝对没有办法开始预测如果他们混合可能会发生什么.
看来你的问题确实是"为什么堆腐败不会发生?".答案就是"因为堆管理器会跟踪分配的块大小".让我们回到C一分钟:如果你想在C中分配一个int,你可以这样做int* p = malloc(sizeof(int))
,如果你想分配大小的数组,n
你可以写int* p = malloc(n*sizeof(int))
或int* p = calloc(n, sizeof(int))
.但无论如何free(p)
,无论你如何分配它,你都可以释放它.你永远不会将大小传递给free(),free()只是"知道"要释放多少,因为malloc() - ed块的大小保存在块的"前面"某处.回到C++,new/delete和new []/delete []通常用malloc来实现(尽管它们不一定是这样,你不应该依赖它).这就是为什么new []/delete组合不会破坏堆的原因 - 删除将释放适量的内存,但是,正如我之前的每个人所解释的那样,你可以通过不调用正确数量的析构函数来获取泄漏.
也就是说,在C++中推理未定义的行为总是毫无意义的.为什么新的[] /删除组合正常工作,"只"泄漏或导致堆损坏?你不应该这样编码,期间!并且,在实践中,我会尽可能避免手动内存管理 - STL和boost是有原因的.
归档时间: |
|
查看次数: |
6796 次 |
最近记录: |