【问题标题】:Replacing existing raw pointers with smart pointers用智能指针替换现有的原始指针
【发布时间】:2010-08-03 22:47:12
【问题描述】:

注意:这听起来可能很愚蠢。 我有一个使用原始指针的应用程序,应用程序中有很多内存泄漏。

现在我的问题是用智能指针替换现有的原始指针有多容易。 替换它们有助于减少由于不释放动态分配的内存而导致的内存泄漏。

进一步解释一下,这个应用程序是一个遗留应用程序,存在非常直接的内存泄漏,内存将被分配并且不会在同一函数本身中释放。

我使用 DevPartner 进行了内存分析,发现了很多区域。 Valgrind 比 Devpartner 更好吗?

【问题讨论】:

    标签: c++ boost


    【解决方案1】:

    使用智能指针无疑是清理应用程序的良好开端,但它们并不是万能的。在一个设计良好的程序中,大量的内存泄漏可能只是粗心大意,但更有可能的是你有重大的设计问题,而内存泄漏就是其中的一个症状。当您切换到智能指针时,您仍然需要做出设计选择,例如“谁拥有这个对象”、“这个对象的所有权是否在多个客户端之间共享”和“这个对象的预期生命周期是多少”,以便选择给定场景的正确智能指针实现。但这将是一个很好的开始方式,因为为不同情况选择合适的智能指针风格的过程将迫使您考虑这些事情,并且可能会改进您的设计。

    【讨论】:

      【解决方案2】:

      毫无疑问,智能指针减轻了程序员的很多负担,因为内存所有权的麻烦。由于您没有提到这是否是一个遗留应用程序或者更改接口有多容易(并不总是需要,因为智能指针确实会降级为原始指针),我建议您在 valgrind 之类的工具下运行您的应用程序(在 Linux ) 或 purify (在 Unices 和 Windows 上) 以跟踪内存泄漏。

      根据我的经验,大多数内存泄漏都遵循某种模式(开发人员 A 遗漏了某些内容,开发人员 B 复制了该代码以及随之而来的问题)。因此,在考虑使用智能指针之前,使用工具也许可以先解决内存泄漏问题。

      如果您开始开发此应用程序 - 从使用智能指针开始。它们将为您节省大量时间和精力。

      【讨论】:

        【解决方案3】:

        我会“非常有可能”给出一个坚定的结论。

        一种形式的内存泄漏是当一个函数分配内存并且没有在至少一个函数外的路径中删除它。某种范围的指针,如auto_ptr,可能会很好地处理这个问题。 (注意:auto_ptr 是第一个 ISO 标准的一部分,并且在下一个标准中被弃用,因此很难就使用什么作用域指针提供建议。请参阅您的编译器和库文档以查看它们支持的内容。)

        另一种形式是分配对象并共享所有权,这意味着有多个例程使用它,并且没有简单的方法来判断每个人是否都使用它。这是shared_ptr 的一个很好的候选,但你必须小心。创建对象时分配给shared_ptr,并确保所有其他使用的指针都是shared_ptr。如果例程 A、B 和 C 通过 shared_ptr 访问对象,而您没有更改 D,那么当例程 A、B 和 C 完成时,它就会消失,而不管 D 的需要。

        使用shared_ptr 要避免的一件事是循环,因为如果我有一个shared_ptr 到J,J 有一个shared_ptr 到K,而K 有一个shared_ptr 到我,这些都不是永远不会被删除,即使程序中的其他任何地方都无法访问。您需要注意这些情况。您可以在循环中使用weak_ptr 将它们分解,或者自己删除循环中的元素,或者只是忍受泄漏。

        要考虑的另一件事是将vector 和类似的容器替换为动态分配的数组和其他动态分配的数据结构。这为您提供了大量免费的内存管理,尽管您仍然可以泄漏内存。如果一个向量变大,然后变小,它不会释放额外的内存。如果你真的需要回收内存,通常的做法是 swap 曾经很大的向量及其自身的副本。

        简而言之,有很多非常有用的工具可以自动执行大量内存管理,但它们并不能取代您的思考。只需将每个指针转换为shared_ptr,将每个数组转换为vector,就可以解决很多问题,但明智地使用它们会带来更大的好处。

        【讨论】:

          【解决方案4】:

          这取决于,如果对对象的硬引用阻止垃圾收集器释放它,垃圾收集程序仍然可能存在内存泄漏。如果您想找出内存泄漏发生在哪里,请使用分析器或编写单元测试和模拟对象。

          编辑:要获得智能指针的好处,您还必须挂钩或实现自己的垃圾收集器,因为它不是语言功能。

          编辑 2:智能指针显然实现了引用计数,这是一种垃圾收集策略。

          【讨论】:

          • 你不需要任何带有智能指针的垃圾收集器,只要你的模型中没有循环。你只需要用普通指针打破循环。我使用智能指针很多年了,从来不需要实现 gc 也没有任何内存泄漏
          【解决方案5】:

          智能指针“在代码中记录”对象的所有权。因此,对于每个指针,您必须将“某些程序员头脑中的模糊文档”转换为“代码中的精确文档”。大多数选择都很容易。这仍然留下了很多不是的选择。此外,除了所有权之外,您还必须担心使用弱指针破坏对象图循环。所以这种向智能指针的整体转换并不是一件容易的事,如果你把循环中断部分弄错了,你甚至会引入新的内存泄漏。

          【讨论】:

          • 智能指针记录的唯一内容是:“我不在乎谁拥有这个对象,只要确保在最后正确销毁它”。你不知道谁引用了这个对象,你也不知道最后一个引用什么时候会超出范围,所以你不知道对象什么时候会被销毁。总而言之,您不知道对象的生命周期,因为没有单一的所有者。每个人都拥有该对象(因为每个客户端都可以销毁它),因此没有人拥有该对象。所以根本没有记录“所有权”。
          • 我认为您对将“所有权”等同于“仅由一个对象拥有所有权”感到有些困惑。大多数代码都涉及(显式或隐式)共享所有权。当您说(因为每个客户端都可以销毁它)时,使用智能指针没有人可以销毁它,只需释放所有权即可。
          • @Frerich 您似乎指的是一种特定类型的智能指针,一种引用计数指针,但是针对不同的所有权场景有不同类型的智能指针。见boost.org/doc/libs/1_43_0/libs/smart_ptr/smart_ptr.htm。如果在给定情况下选择了智能指针的风格,它是在代码中记录对象所有权策略的绝佳方式。
          • @bshields:没错,我想到了引用计数指针。我应该明确指出这一点。
          猜你喜欢
          • 2016-08-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多