【问题标题】:When to delete objects from the heap in C++?何时从 C++ 中的堆中删除对象?
【发布时间】:2012-06-02 09:51:18
【问题描述】:

我有点困惑何时/如何在 C++ 中从堆中删除对象。

时间:

如果您正在执行一个相对较短的程序,计算一些东西然后将标准输出传递到控制台,是否值得在控制台输出结果后立即销毁所有对象,或者程序会在退出时自动销毁?对于大型程序,我认为当您不再需要任何对象时尝试解决肯定是更好的做法?

如何:

如果我有一个包含指向 MyClass 对象的指针的向量,一旦我完成了向量(和 MyClass 对象),我该如何编写一个可以销毁向量指向的所有 MyClass 对象的析构函数? (显然我也需要销毁向量)。

【问题讨论】:

  • 对于您的How,如果向量本身在堆栈上,则不需要销毁向量本身。要删除内部的每个指针,请遍历并在每个元素上调用 delete。不过,您应该选择智能指针。
  • 如果程序在短时间内进行一些计算然后退出,我认为您不必担心释放对象,因为程序持有的每个资源都会在程序被释放后立即释放退出。
  • 虽然人们会告诉你,泄漏是可以的,因为短程序养成了分配和删除的习惯。这是个好习惯
  • “这是风格问题……草率的工作往往会形成习惯。” – David Eddings 的 魔法城堡 中的丝绸。
  • 我只是想回应拉尔斯曼上面的评论。如果您以需要显式调用 delete(或 delete[])来释放内存的方式进行编程,那么您将遭受大量不必要的痛苦。您花在学习智能指针(尤其是 shared_ptr)和 RAII 编程风格上的时间是一项投资,它将以节省时间的形式获得很多倍的回报,因为您不必跟踪由于过早导致的内存泄漏和/或崩溃释放的对象。养成使用智能指针的习惯,你不会后悔的。

标签: c++


【解决方案1】:

对于短期运行的程序,可以不删除对象。没有不良后果。

但设计良好的应用程序代码是可重用的,而可重用的代码基本上是库代码。库代码应该删除它分配的对象,因为它可能用于长时间运行的程序中。

所以换句话说,不要为一个一次性的小程序担心它。但是对于严肃的代码,删除对象而不是泄漏它们。

【讨论】:

  • 练习还是不错的。 Windows API 中的某些内容可能在程序结束后仍然存在。我认为顶点缓冲区就是其中之一,但我可能记错了。 DirectX 中的一些东西让我想起了。
  • –1,如果可以的话,我会投反对票更多:这是草率的编程。这是个坏建议。不要这样做——即使是短节目也不行……尤其是因为这样做没有任何好处。如果您对清洁过敏,请不要产生垃圾。
  • 平心而论,它可以有好处,但在小程序中却没有。由于需要运行所有析构函数,大型、复杂的程序可能需要很长时间才能关闭。在这些情况下,绕过它们并只是泄漏内存(操作系统无论如何都会回收)可能更可取。但是,这与此处建议的情况截然不同(基本上是“不要费心学习/养成正确管理记忆的习惯”)。默认情况下,您应该正确处理内存和其他资源。在特殊情况下,您可能会选择偏离此设置
  • @chris:据我所知,存在全局 COM 堆,它确实独立于进程,操作系统无法回收从中分配的内存。但那是我模糊的记忆。
  • @jalf 如果设置了指示应用程序关闭的全局标志,则执行该优化的一种更优雅的方法是将 ::operator delete 覆盖为 no-op。
【解决方案2】:

如果您在使用完对象后没有销毁它们,那么您将面临内存/资源泄漏。如果您失去了对对象的跟踪,那么您就有了泄漏。

这是长时间运行的程序中的一个问题,因为您可能会耗尽内存或资源。如果没有正确跟踪对象,这在任何程序中都是一个不好的迹象。物件是你造的,为什么不能也毁掉呢?

大多数(每个?)操作系统都会在进程退出时从进程中回收分配的内存,因此您无需自行整理。在某些情况下,避免这种毫无意义的整理可能意味着您的用户在您的流程完成时不会等待太久。

如果您的程序具有这样的行为,它会降低您的代码可重用性。如果你想将它打包到一个库中,你将不得不处理这个问题,因为你无法提前知道你的库将如何被使用。

至于您的指针向量:迭代向量并在每个指针上调用 delete。然后销毁向量。

【讨论】:

    【解决方案3】:

    这是一个非常简单的契约——所有编程中最简单的契约之一:如果你分配一个资源,你就向 API 承诺你会适当地释放它。

    不这样做产生不良后果。从浪费堆空间到 nasal demons 啃你的内脏。

    事实上,没有理由不释放您请求的资源。不这样做是草率的,我认为这表明代码质量普遍较差(因为它!)。

    此外,C++ 让不必做出这个决定变得非常容易:只要不产生垃圾,就不需要清理。不要分配 freestore 内存,或者如果你必须这样做,使用智能指针或特殊分配器来管理它。

    如果您正确使用 C++,根本没有令人信服的技术理由来草率使用资源,并且有足够的动力去关注。

    至于“如何”,我首先质疑存储指针的决定:原始 C++ 指针不应该拥有内存。如果您确实需要 freestore 内存,请使用智能指针,或者(最好)使用自动对象:因为它们位于向量中,这已经为您提供了所有权和存储位置,并且向量类会自动处理该内存。

    如果您需要多态对象,不幸的是您需要存储指针。在这种情况下,有多种可能性,但最简单的可能是使用智能指针。

    【讨论】:

    • 当然最后一段只适用于实际的运行时多态性。在许多语言中,多态是唯一的一种,但在 C++ 中,它通常可以通过模板推送到编译时,然后就不需要显式存储指针了。
    • 康拉德是对的。我的回答不清楚:首先要学习的是正确管理您的资源。如果(且仅当)您有一个特定情况,即强制退出比正确清理有显着优势,并且您有数字证明这一点,那么您的罪孽可能会被宽恕!
    • @Konrad “我会质疑存储指针的决定”你能详细说明一下吗?
    • @user997112 就像我说的,原始指针永远不应该拥有内存,有更好的工具。一般来说,don’t use pointers.
    • @leftaroundabout 你当然是对的,但术语“多态对象”(与“多态”相反)通常专门用于具有虚拟方法的对象。
    【解决方案4】:

    如何编写一个析构函数来销毁向量指向的所有 MyClass 对象?

    非常好的问题!具有这种析构函数的类称为智能指针,它们由 Boost、TR1 和 C++11 标准库等库提供,其中至少一个可能随您的平台提供。

    您几乎不应该直接使用new T。将unique_ptr<T> 用于直接所有权(当且仅当向量仍然存在时,对象才会保留)或shared_ptr<T> 用于共享所有权。这些模板存在于命名空间boost::std::tr1::std:: 中,具体取决于您获取它们的方式。在任何情况下,接口都大致相同。

    当然,如果可以避免的话,你根本不应该使用任何类型的指针。 vector<T>vector< unique_ptr< T > > 更可取。

    至于是否在退出前释放资源,当然应该。良好的 C++ 实践使得忽视释放资源比正确做事更难,所以这并不是我们真正担心的事情。

    【讨论】:

    • 我用的不是C++11,用auto_ptr够吗?
    • 你仍然可以使用 boost。比auto_ptr好多了。
    • @user997112 auto_ptr 不能存储在容器中,所以它不能解决这个问题,它已被弃用。您几乎可以肯定已经有了可用的 TR1,或者如果您更喜欢使用 Boost。
    猜你喜欢
    • 2013-05-12
    • 2021-11-10
    • 2013-03-05
    • 2014-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多