【问题标题】:new() without delete() is Undefined Behavior or merely Memory Leak? [duplicate]没有 delete() 的 new() 是未定义的行为还是仅仅是内存泄漏? [复制]
【发布时间】:2012-04-12 20:09:30
【问题描述】:

可能重复:
Are memory leaks “undefined behavior” class problem in C++?

在 C++ 程序中从不调用 newnew [] 返回的地址上的 deletedelete[] 是未定义行为还是仅仅是内存泄漏?

欢迎参考标准(如果有)。
这出现在其中一个 cmets here 中,我对此有点困惑。

【问题讨论】:

  • 你为什么会认为它是未定义的行为。我在标准中没有看到任何表明这会导致这个结论的东西。要求证明否定是不可能的(你只能证明肯定)。
  • 只在时间结束时出现的UB?
  • @DavidSchwartz 不,即使那样。
  • @LokiAstari:嗯,正如我已经提到的那样我对此有点困惑。我明确指出来自标准的参考(如果有的话)我>。我不明白问这个问题有什么问题?而且我不认为它应该仅仅为了它而被否决或批评。
  • @sharptooth 这适用于 c++03。 c++11 和 3.8/4 呢?

标签: c++ memory-leaks new-operator undefined-behavior


【解决方案1】:

[basic.life](3.8 对象生命周期)在第 4 段中告诉:

程序可以通过重用对象占用的存储空间或通过显式调用具有非平凡析构函数的类类型对象的析构函数来结束任何对象的生命周期。对于具有非平凡析构函数的类类型的对象,在重用或释放对象占用的存储空间之前,程序不需要显式调用析构函数;但是,如果没有显式调用析构函数或者如果没有使用删除表达式 (5.3.5) 来释放存储,则不应隐式调用析构函数,并且生成任何依赖于副作用的程序由析构函数未定义 行为。

【讨论】:

  • +1 - 现在定义“取决于”。
  • +1 - 这最终表明它是一个 UB,除非有人证明“依赖于”使它不适用于手头的问题。
  • @Als 这最终表明它不是未定义的行为,除非您的程序逻辑取决于您在析构函数中实现的某些内容。大多数析构函数没有副作用,因此该段落根本不适用。但即使对于那些这样做的人,您的代码也可能不依赖它们才能正常运行。
  • 感谢您的回答以及@JamesKanze,我的 Q 得到了回答,因为您是提供标准报价的人,所以我会接受您的回答。
  • 此外,此引用仅适用于在不调用析构函数的情况下重用或释放内存的情况。我不确定程序退出是否构成“重用或释放”内存。显然从操作系统的 POV 来看,内存会被释放,但我认为这超出了运行程序的范围,因此超出了 C++ 标准的范围。
【解决方案2】:

关于newdelete 的语义标准是明确的。如果您不调用delete,肯定没有未定义的行为;事实上,这是单例的标准做法,我想std::coutstd::cin 使用new[] 来获取他们的缓冲区(他们几乎肯定不会delete)。为什么调用delete 不是未定义行为?

未定义的行为是调用错误形式的delete,调用free 以获得分配给new 的内存,或者通常尝试删除对象而不遵循其分配所需的协议。

【讨论】:

  • @Als 显然,如果你需要析构函数的语义,那么你需要删除对象。同样明显的是,如果您不想要析构函数的语义,那么最好不要调用 delete。引用的段落没有说出常识不会告诉你的任何内容。
  • +1 我理解你试图通过你的 cmets 表达的观点。我相信所有这些 cmets 加上 @VJovic 添加的引用最终回答了我的问题。谢谢。
【解决方案3】:

参考 [basic.stc.dynamic.deallocation](在 n3337 中又称为 3.7.4.2)只有 4 个段落。

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

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

我认为标准本身并不关心这一点,因此它更未指定而不是未定义

【讨论】:

  • 不调用 delete 本身不会引发未定义的行为。等待析构函数调用确实不会发生。 VJovic 肯定得到了正确的引用。
【解决方案4】:

这只是内存泄漏。

但我清楚地记得使用newdelete[]new []delete 的标准是未定义的行为。 (或与mallocfree 的任意组合)

我不认为标准明确规定,如果您未能调用 delete,则调用 new 会导致未定义的行为。另外,运行时如何判断您是稍后调用delete 还是根本不调用它?

我认为标准中没有任何合同规定 - 如果您执行 X,则之后必须执行 Y,否则就是 UB。

【讨论】:

  • 这甚至不一定是内存泄漏。这可能是故意的,以便在程序关闭期间可以继续使用该对象。
  • @JamesKanze 这值得商榷。 :) 即使是故意的,我也不明白这不是内存泄漏。我明白你的意思,我只是说即使对象一直存在到运行结束,但没有被释放......也许这也是内存泄漏?我知道操作系统会回收内存,但是……嗯……语义
  • @JamesKanze:当操作系统必须释放你的对象时(因为你没有释放/删除它),它就是泄漏。但是,泄漏可能是故意的。
  • 内存泄漏是内存泄漏的代码。不断地。您不删除某些内容的事实本身并不是内存泄漏。你不断分配你不删除的东西的事实是。内存泄漏迟早是致命错误。并且可以说,像编译器这样的短期程序不会泄漏(我见过一个用 C 编写的编译器,它从未调用过free)。
  • @SigTerm 操作系统总是需要清理你的内存,因为operator delete 通常不会将内存返回给操作系统。
【解决方案5】:

假设如果您不调用 delete,您的程序仍然可以运行。但是,如果您不删除内存分配,您的程序内存使用量将持续增长,直到您的程序耗尽可用内存(运行时间越长,发生这种情况的机会就越大),这将导致不同点的崩溃并且非常很难检测到(我认为评论中提到的“未定义行为”是什么意思)

【讨论】:

  • UB 在 C++ 的上下文中具有非常特殊的含义,而这个含义正是评论者所想的。
  • 首先,某些分配永远不会被删除是完全正常的。内存泄漏是指您保留永远不会使用的已分配内存。程序耗尽堆并不是未定义的行为——标准要求new 抛出std::bad_alloc。 (当然,有些系统使用堆和栈的公共空间,使用过多的堆会导致堆栈溢出,这是未定义的行为。)
【解决方案6】:

如果没有为new/new[] 分配的对象调用delete/delete[],则会出现资源泄漏。如果构造函数分配了动态内存,则可能是内存泄漏。如果构造函数分配了信号量锁、未释放文件句柄等其他事情,则可能会发生。

这不会是未定义的行为。

【讨论】:

  • 这不能回答我的问题。
  • @Als 这个答案告诉你删除可以做的不仅仅是只是内存释放。对象控制的资源可能不会被释放。例如,在将放射性棒移入或移出堆芯时,建造者可能会拆除核电站中放射性棒上的安全闩锁。析构函数可能是重新应用闩锁的地方。重要。
  • @PeterWood:这是一个很好的观点,我已经知道并且没有回答我问的问题。我的问题是不是是否delete只有 是否释放内存。
【解决方案7】:

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

【讨论】:

  • 不正确。它会在应用程序的生命期间导致资源泄漏,退出后它将返回给操作系统(并且任何现代操作系统都可以在之后检索所有内存进程退出),即使不是所有内存都得到了delete'd。
  • 是的,这是正确的夜行者,资源泄漏只会在应用程序的生命周期内发生。应用程序结束时资源将被回收。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-12
  • 1970-01-01
  • 2011-05-15
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多