知道什么引用了一个对象

时间:2010-01-10 08:12:10

标签: c++ reference-counting

我有一个实现引用计数机制的对象。如果对它的引用数变为零,则删除该对象。

我发现我的对象永远不会删除,即使我完成了它。这导致内存过度使用。我所拥有的只是对象的引用数量,我想知道引用它的位置,以便我可以编写适当的清理代码。

有没有办法在不必grep源文件的情况下完成此操作? (这将非常麻烦。)

5 个答案:

答案 0 :(得分:6)

在C ++中正确完成引用计数(引用计数)的很大一部分是使用Resource Allocation Is Initialization ,因此不小心泄漏引用更加困难。但是,这并不能通过refcounts解决所有问题。

也就是说,您可以在引用计数中实现一个调试功能,该功能可以跟踪保存引用的内容。然后,您可以在必要时分析此信息,并将其从发布版本中删除。 (使用与DEBUG宏的使用方式类似的配置宏。)

您应该如何实现它将取决于您的所有要求,但有两种主要方法(简要介绍差异):

  • 将信息存储在引用的对象本身上
    • 可从调试器访问
    • 更容易实施
  • 每次获取或释放引用时输出到特殊跟踪文件
    • 程序退出后仍然可用(甚至异常)
    • 可以在程序运行时使用,而无需在调试器中运行
    • 甚至可以在特殊版本构建中使用并发送给您进行分析

知道什么是引用给定对象的基本问题一般难以解决,并且需要一些工作。比较:您能告诉我知道您的邮政地址或电话号码的每个人和企业吗?

答案 1 :(得分:2)

引用计数的一个已知缺点是当存在循环引用时它不起作用,即(在最简单的情况下)当一个对象具有对另一个对象的引用时,该另一个对象又引用前一个对象。这听起来像一个非问题,但在数据结构中,例如二进制树,对父节点的反向引用,你就是。

如果您没有在引用的(未释放的)对象中明确提供“反向”引用列表,我看不出找出引用它的人的方法。

在以下建议中,我假设您不想修改您的来源,或者如果是这样,只是一点点。

您当然可以遍历整个堆/ freestore并搜索未释放对象的内存地址,但如果其地址出现,则无法保证 内存地址引用;它也可以是任何随机的浮点数。但是,如果找到的值位于一个块内部,那么应用程序为一个对象分配了一个内存,可能会稍微提高一点,它实际上是指向另一个对象的指针。

这种方法的一个可能的改进是修改你使用的内存分配器 - 例如您的全局operator new - 以便它保留所有已分配内存块及其大小的列表。 (在完整的实现中,operator delete将删除已释放的内存块的列表条目。)现在,在程序结束时,您有一个线索 where 可以搜索对于未释放对象的内存地址,因为您有一个程序实际使用的内存块列表

上述建议对我来说听起来不太可靠,说实话;但是可能定义一个自定义全局operator newoperator delete,它会执行一些记录/跟踪,以便解决您的问题。

答案 2 :(得分:1)

我假设您有一些说addRef()release()成员函数的类,当您需要增加和减少每个实例的引用计数时,您可以调用它们,并且这些实例会导致问题在堆上并用原始指针引用。最简单的修复可能是用boost::shared_ptr替换受控对象的所有指针。这很容易做到,应该让你省去自己的引用计数 - 你可以让我提到的那些功能什么都不做。代码中所需的主要更改是传递或返回指针的函数的签名。其他要更改的地方是初始化列表(如果初始化指向null的指针)和if() - 语句(如果将指针与null进行比较)。更改指针的声明后,编译器将找到所有这些位置。

如果您不想使用shared_ptr - 也许您希望保持该类固有的引用计数 - 您可以创建自己的简单智能指针来处理您的类。然后使用它来控制类对象的生命周期。因此,例如,不是使用原始指针完成指针分配而是“手动”调用addRef(),而只是自动分配包含addRef()的智能指针类。

答案 3 :(得分:0)

我认为没有代码更改就可以做某事。通过代码更改,您可以记住增加引用计数的对象指针,然后查看剩下的指针并在调试器中检查它。如果可能 - 存储更详细的信息,例如对象名称。

答案 4 :(得分:0)

我已根据自己的需要创建了一个。您可以将您的代码与此代码进行比较,看看缺少什么。它并不完美,但它应该适用于大多数情况。 http://sites.google.com/site/grayasm/autopointer

当我使用它时,我这样做:

util::autopointer<A> aptr=new A();

我从来没有这样做过:

A* ptr = new A();
util::autopointer<A> aptr = ptr; 

后来开始用ptr开始;这是不允许的。 另外我只使用aptr来引用这个对象。 如果我错了,我现在有机会得到更正。 :)见啊!