如何配对新的与删除可能导致内存泄漏只?
首先,根据C ++标准,对于分配了new[]
东西,使用delete
是未定义的行为。
在Visual C ++ 7中,这样的配对可能会导致两个后果之一。
如果new []'ed具有简单的构造函数和析构函数,VC ++只是使用new
而不是new[]
并且使用delete
来工作正常 – new
调用“allocate memory”, delete
只调用“free memory”。
如果new []'ed有一个不平凡的构造函数或析构函数,那么上面的技巧就无法完成了 – VC ++ 7必须调用正确数量的析构函数。 所以它在数组前面加上一个size_t
存储元素的个数。 现在,由new[]
返回的地址指向第一个元素,而不是指向块的开始。 所以如果使用delete
,它只会调用第一个元素的析构函数,并调用与“allocate memory”返回的地址不同的地址的“空闲内存”,这会导致HeapFree()中的一些错误指示,我怀疑是指堆腐败。
然而,在这里和那里,人们可以阅读在new[]
之后使用delete
导致内存泄漏的错误语句。 我怀疑任何大小的堆腐败比仅仅为第一个元素调用析构函数的事实重要得多,可能没有调用的析构函数没有释放堆分配的子对象。
在new[]
之后如何使用delete
可能导致某些C ++实现中的内存泄漏?
假设我是一个C ++编译器,并且我实现了像这样的内存pipe理:我将每块保留内存与内存大小相加,以字节为单位。 像这样的东西;
| size | data ... | ^ pointer returned by new and new[]
请注意,就内存分配而言, new
和new[]
之间没有区别:只是分配一定大小的内存块。
现在怎么会delete[]
知道数组的大小,为了调用正确的析构函数呢? 用sizeof(T)
简单地划分内存块的sizeof(T)
,其中T
是数组元素的types。
现在假设我只是对析构函数进行一次调用,然后释放size
字节,那么后续元素的析构函数将永远不会被调用。 这导致泄漏后续元素分配的资源。 然而,因为我自由size
字节(不是sizeof(T)
字节),没有堆损坏发生。
关于混合new[]
和delete
涉嫌造成内存泄漏的童话故事只是:童话故事。 它在现实中绝对没有立足之地。 我不知道它是从哪里来的,现在它已经获得了自己的生命,像病毒一样生存下来,通过口口相传,从一个初学者传播到另一个。
这个“内存泄漏”背后的最可能的基本原理是,从无辜天真的angular度来看, delete
和delete[]
的不同之处在于, delete
仅用于销毁一个对象,而delete[]
销毁对象数组(“很多“的对象)。 通常由此得出的一个天真的结论是,数组的第一个元素将被delete
,而其余的将继续,从而造成所谓的“内存泄漏”。 当然,任何对典型堆实现至less有基本理解的程序员都会立即明白,最可能的后果是堆腐败,而不是“内存泄漏”。
对于“内存泄漏”理论的另一个stream行解释是,由于被调用的析构函数数量错误,数组中的对象拥有的辅助内存不会被释放。 这可能是事实,但这显然是一种非常强制性的解释,在面对更为严重的堆腐败问题时,这种解释几乎没有意义。
简而言之,混合不同的分配函数是导致不确定的 ,非常实际的未定义行为的错误之一 。 任何试图对这种不明确行为的performance施加某种具体限制的做法都只是浪费时间,并且显然缺乏基本的了解。
不用添加, new/delete
和new[]/delete[]
实际上是两个独立的内存pipe理机制,可独立定制。 一旦它们被定制(通过replace原始的内存pipe理function),如果它们混在一起,甚至不可能开始预测会发生什么。
看来你的问题真的是“为什么堆腐败没有发生?”。 答案是“因为堆pipe理器跟踪分配的块大小”。 让我们回到C一分钟:如果你想在C中分配一个int,你可以做int* p = malloc(sizeof(int))
,如果你想分配大小为n
数组,你可以写int* p = malloc(n*sizeof(int))
或int* p = calloc(n, sizeof(int))
。 但无论如何,无论您如何分配,都可以free(p)
它。 你永远不会把size(),free()只是“知道”释放多less,因为malloc()块的大小被保存在块的前面。 回到C ++,new / delete和new [] / delete []通常是以malloc的forms实现的(虽然不是必须的,但不应该依赖于这个)。 这就是为什么新的[] /删除组合不会损坏堆 – 删除将释放正确的内存量,但正如我之前所有人所解释的,通过不调用正确数量的析构函数可以得到泄漏。
也就是说,推理C ++中的未定义行为总是毫无意义的。 为什么新的[] /删除组合恰好工作,“只”泄漏或导致堆腐败? 你不应该这样写代码! 而且,在实践中,我会尽可能避免手动内存pipe理 – STL和boost是有原因的。
如果除了数组中的第一个元素之外的所有非调用析构函数都应该释放一些内存,则会导致内存泄漏,因为这些对象没有正确清理。
如果析构函数释放内存的话,将会导致C ++的所有实现中泄漏,因为析构函数永远不会被调用。
在某些情况下,它可能导致更糟糕的错误。
如果new()运算符被覆盖,但new []不是,则可能发生内存泄漏。 删除/删除[]操作符也一样
除了导致未定义的行为之外,最直接的泄漏原因在于除了数组中的第一个对象外,其他所有的析构函数都没有调用析构函数。 如果对象已经分配了资源,这显然会导致泄漏。
这是我能想到的最简单的类,导致了这种行为:
struct A { char* ch; A(): ch( new char ){} ~A(){ delete ch; } }; A* as = new A[10]; // ten times the A::ch pointer is allocated delete as; // only one of the A::ch pointers is freed.
PS:请注意,构造函数也无法在很多其他编程错误中调用:非虚拟基类析构函数,错误依赖智能指针,…
迟到的答案,但…
如果你的删除机制只是简单地调用析构函数,并将释放的指针和sizeof所蕴含的大小一起放到一个空闲的堆栈上,那么在一个新的[]分配的内存块上调用delete会导致内存丢失,没有腐败。 更复杂的malloc结构可能会破坏或检测到这种行为。
为什么不能这样做呢?
显然内存泄漏是否发生堆损坏。
或者说,因为我可以重新实施新的和删除…..不能它不会造成什么。 从技术上讲,我可以导致新的和删除执行新的[]和删除[]。
HENCE:未定义的行为。
我正在回答一个被标记为重复的问题,所以我只是在这里复制它,以防万一。 有人说我的内存分配的方式,我只是解释原因和影响。
只是一个小东西closures谷歌: http : //en.cppreference.com/w/cpp/memory/new/operator_delete
无论如何, 删除是一个单一的对象的function。 它从指针中释放实例,然后离开;
delete []是一个用来释放数组的函数。 这意味着,它不只是释放指针; 它声明该数组的整个内存块为垃圾。
这在实践中很酷,但你告诉我你的应用程序的作品。 你可能想知道… 为什么?
解决scheme是C ++不能修复内存泄漏 。 如果你使用删除没有括号,它会删除只是作为一个对象的数组 – 一个可能导致内存泄漏的进程。
很酷的故事,内存泄漏,我为什么要关心?
分配的内存不会被删除时发生内存泄漏。 那么内存就需要不必要的磁盘空间,这会让你几乎没有理由丢失有用的内存。 这是不好的编程,你应该修复它在你的系统。