2010-08-03 153 views
1

注意:这可能听起来很愚蠢。 我有一个应用程序使用原始指针,并且应用程序中存在大量内存泄漏。用智能指针替换现有的原始指针

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

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

我使用DevPartner完成了一次内存分析,发现了很多区域。 Valgrind是否比Devpartner更好?

回答

3

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

1

这取决于,如果对对象的硬引用阻止垃圾收集器释放垃圾回收程序,垃圾收集程序仍然可能存在内存泄漏。如果你想知道内存泄漏发生在哪里,可以使用一个分析器或写单元测试和模拟对象。

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

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

+1

智能指针与垃圾收集不同。 – 2010-08-03 13:46:21

+0

啊,好点。注意:-) – gtrak 2010-08-03 13:58:14

+1

只要你的模型没有循环,你就不需要任何带有智能指针的垃圾回收器。你只需要用普通的指针来打破这个循环。我用了多年的智能指针,从来没有实现gc也没有任何内存泄漏 – mb14 2010-08-03 13:59:58

2

毫无疑问,智能指针可以减轻程序员内存所有权麻烦的负担。既然你没有提到这是一个遗留应用程序还是它改变接口有多容易(因为智能指针会降级到原始指针,所以并不总是需要),我建议你使用valgrind之类的工具来运行你的应用程序(在Linux上)或纯化(在Unices和Windows上)来跟踪内存泄漏。

根据我的经验,大多数内存泄漏遵循某种模式(开发人员A错过了一些东西,开发人员B复制了该代码并且伴随着问题)。因此,在考虑使用智能指针之前,使用工具可能会首先解决内存泄漏问题。

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

0

智能指针'代码'拥有对象中的文档。所以对于每一个指针,你必须将'在一些程序员头部的模糊文档'转换成'精确的代码文档'。大多数选择都很简单。这仍然留下了很多不是的选择。除了所有权以外,您还必须担心使用弱指针来破坏对象图循环。因此,这种整体转换为智能指针并不是一项简单的任务,如果循环中断部分错误,您甚至会引入新的内存泄漏。

+0

智能指针文档的唯一内容是:“我不在乎谁拥有这个对象,只是确保在最后正确销毁它”。你不知道谁引用了这个对象,你也不知道最后一个引用何时会超出范围,所以你不知道对象何时会被销毁。总而言之,你不知道对象的生命周期,因为没有一个所有者。每个人都拥有这个对象(因为每个客户都可以销毁它),所以没人拥有这个对象。所以“所有权”根本没有记录。 – 2010-08-03 14:50:29

+0

我认为你把'所有权'和'只有一个对象所有权'等同起来会有点困惑。大多数代码涉及(明示或暗示)共享所有权。当你说(因为每个客户都可以销毁它),用智能指针没有人可以销毁它,只需释放所有权。 – jyoung 2010-08-03 14:59:58

+0

@Frerich你似乎指的是一种特定类型的智能指针,一种引用计数的指针,但是对于不同的所有权场景,有不同类型的智能指针。请参阅http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/smart_ptr.htm。如果智能指针的风格在特定情况下被选择得很好,那么它就是在代码中记录对象所有权策略的绝佳方式。 – bshields 2010-08-04 13:57:50

2

我打算给这个“非常可能”的公司。

内存泄漏的一种形式是当函数分配内存并且至少在函数外的一个路径中不删除它。某种范围的指针,如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即可解决很多问题,但智能地使用这些指标会带来更大的好处。