new()没有delete()是未定义行为还是仅仅是内存泄漏?

3

3
为什么您认为这是未定义行为?我没有在标准中看到任何表明会导致这个结论的迹象。要求证明否定是不可能的(只能证明肯定)。 - Martin York
只在时间结束时发生的 UB? - David Schwartz
@DavidSchwartz 不,即使那样也不行。 - BЈовић
@sharptooth 这适用于C++03。那么对于C++11和3.8/4呢? - BЈовић
@SteveJessop:我忘记加笑脸了 :-) 我试着写一个证明,但它变成了一篇关于踢猫的论文(因为我的老教授用这个比喻来解释)。但我无法得到正确的答案,所以它看起来非常残酷。 - Martin York
显示剩余6条评论
7个回答

9
标准对于newdelete的语义非常清晰。如果您不调用delete,那么肯定不会有未定义的行为;事实上,对于单例来说,这是标准做法。我想std::coutstd::cin使用new[]来获取它们的缓冲区(几乎肯定永远不会delete)。为什么不调用delete会导致未定义的行为呢?
未定义的行为是指调用错误形式的delete、对使用new分配的内存调用free,或者在没有遵循其分配所需的协议的情况下尝试删除对象。

1
@Als 显然,如果您需要析构函数的语义,那么您需要删除该对象。同样显然,如果您不想要析构函数的语义,那么最好不要调用delete。引用的段落并没有说出任何常识告诉您的内容。 - James Kanze
+1 我理解您通过评论想要表达的观点。我相信所有这些评论加上 @VJovic 引用的内容,综合起来对我的问题做出了确凿的回答。感谢。 - Alok Save

9

[基本生命周期](3.8 对象生命周期)第4段告诉我们:

程序可以通过重用对象占用的存储空间或显式调用具有非平凡析构函数的类类型对象的析构函数来结束任何对象的生命周期。对于具有非平凡析构函数的类类型对象,程序不需要在重用或释放对象所占用的存储空间之前显式调用析构函数;然而,如果没有显式调用析构函数,或者没有使用delete-expression(5.3.5)来释放存储空间,则析构函数不得被隐式调用,任何依赖析构函数产生的副作用的程序都具有未定义行为。


+1 - 现在定义“依赖于”。 - Mankarse
+1 - 这明确地表示它是一个未定义行为,除非有人证明“取决于”使其不适用于手头的问题。 - Alok Save
8
这段话的意思是,除非你在析构函数中实现了某些逻辑并且你的程序依赖于这些逻辑,否则这就可以肯定地说明它不属于未定义行为。大多数析构函数没有副作用,所以这段话根本不适用。但即使对于那些有副作用的析构函数,你的代码也可能不依赖它们才能正常运行。 - James Kanze
谢谢您的回答,再加上@JamesKanze的评论,我的问题已得到解答。既然您提供了标准引用,我将接受您的答案。 - Alok Save
1
此外,这个引用仅适用于在不调用析构函数的情况下重复使用或释放内存的情况。我不确定程序退出是否构成“重复使用或释放”内存。显然从操作系统的角度来看,内存将被释放,但我认为这超出了运行程序的范围,因此也超出了C++标准的范围。 - Steve Jessop

1

参考 [basic.stc.dynamic.deallocation](也称为 n3337 中的 3.7.4.2),其中只有 4 段。

  1. operator deleteoperator delete[] 应该是类成员或全局范围内的函数
  2. 关于 operator deleteoperator delete[] 的有效签名的精度
  3. 关于可以用于释放内存的 delete,取决于用于分配内存的 new 的精度
  4. 关于调用的可能参数值和效果的精度(即此存储的指针现在无效)

这里绝对没有说明如果分配了存储空间但从未释放会发生什么。

我认为标准并不关心这个问题,因此它更多是“未指定”的而不是“未定义”的。


不调用delete本身并不会引起未定义的行为。等待析构函数调用永远不会发生。VJovic肯定得到了正确的引用。 - Matthieu M.

0

这只是一个内存泄漏问题。

但我明确记得标准说使用newdelete[],以及new []delete的组合与mallocfree的任何组合都是未定义行为。

我不认为标准特别指出如果您未调用delete,调用new会导致未定义行为。此外,运行时如何知道您稍后调用delete还是根本没有调用它?

我不认为标准中有任何契约规定-如果您执行X,则必须随后执行Y,否则就是未定义行为。


@JamesKanze 这是有争议的。:) 即使是故意的,我也不明白它怎么不是内存泄漏。我明白你的意思,只是说即使对象一直存在到程序运行结束,但没有被释放...也许这也算是一种内存泄漏?我知道操作系统会回收内存,但是...唔...语义方面还是有些问题的。 - Luchian Grigore
@JamesKanze:当操作系统必须释放您的对象(因为您没有释放/删除它)时,就会出现泄漏。然而,这种泄漏可能是有意的。 - SigTerm
1
内存泄漏是指代码不断地泄露内存。仅仅因为你没有删除某些东西并不意味着存在内存泄漏;而是因为你不断地分配一些东西却没有删除它们才会导致内存泄漏。内存泄漏是一个致命的错误,迟早会出现。可以说,像编译器这样的短期程序不可能发生内存泄漏(我曾经见过一个用C语言编写的编译器从未调用“free”函数)。 - James Kanze
@SigTerm 操作系统总是需要清理你的内存,因为 operator delete 通常不会将内存返回给操作系统。 - James Kanze
关于原回答中的最后一段:标准不能提出这样的要求,因为“afterwards”的概念太模糊了——那“永远”不终止的程序呢?(“永远”加引号是因为所有程序都会在某个时候终止,即使只有当它们运行的计算机失败时。) - James Kanze
显示剩余7条评论

0
假设您不调用delete,程序仍将正常工作。但是,如果您不删除内存分配,则程序的内存使用量将不断增长,直到程序耗尽可用内存(运行时间越长,发生这种情况的可能性就越大),这将导致在不同点崩溃,并且很难检测(我认为这就是评论中提到的“未定义行为”)。

在C++的上下文中,“UB”有非常具体的含义,而且这个含义恰好是评论者在那里所想的。 - Jon
首先,有些分配的内存永远不会被删除是完全正常的。内存泄漏是指保留了永远不会使用的已分配内存。程序耗尽堆内存并不是未定义行为——标准要求new抛出std::bad_alloc异常。(当然,一些系统将堆和栈用于共同空间,使用过多的堆可能会导致栈溢出,这是未定义行为。) - James Kanze

0
我不明白不释放内存怎么会导致未定义的行为。如果你不清理,操作系统仍然知道分配的内存。这将导致资源泄漏,只要应用程序运行。

1
不是这样的。这将导致应用程序在运行期间存在资源泄漏,在退出后它将返回给操作系统(任何现代操作系统都能够在进程退出后检索所有内存),即使并非所有内存都被“delete”掉。 - orlp
没错,Nightcracker。资源泄漏只会在应用程序的生命周期内发生。当应用程序结束时,资源将被回收。 - fduff

0
如果使用new/new[]分配对象后没有调用delete/delete[],就会出现资源泄漏。如果构造函数已经分配了动态内存,则可能会出现内存泄漏。如果构造函数分配了其他资源,例如信号量锁未释放、文件句柄未释放等,也可能会发生类似的问题。
这不会导致未定义行为。

这并没有回答我的问题。 - Alok Save
1
@Als 这个答案告诉你,delete 可以做的不仅仅是内存释放。由对象控制的资源可能不会被释放。例如,构造函数可能会在将核反应堆中的放射性棒移入或移出时去除安全锁。析构函数可能是重新应用锁的地方。这一点非常重要。 - Peter Wood
@PeterWood:它提出了一个我已经知道的好观点,但未能回答我所问的问题。我的问题不是delete是否仅释放内存。 - Alok Save

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