我有一个实现引用计数机制的对象。如果对它的引用次数为零,则删除该对象。
我发现我的对象永远不会被删除,即使我完成了它。这会导致内存过度使用。我所拥有的只是对对象的引用数量,我想知道引用它的位置,以便我可以编写适当的清理代码。
有什么方法可以在不必在源文件中使用 grep 的情况下完成此操作?(那会很麻烦。)
我有一个实现引用计数机制的对象。如果对它的引用次数为零,则删除该对象。
我发现我的对象永远不会被删除,即使我完成了它。这会导致内存过度使用。我所拥有的只是对对象的引用数量,我想知道引用它的位置,以便我可以编写适当的清理代码。
有什么方法可以在不必在源文件中使用 grep 的情况下完成此操作?(那会很麻烦。)
在 C++ 中正确完成引用计数(引用计数)的很大一部分是使用资源分配即初始化,因此意外泄漏引用要困难得多。但是,这并不能通过引用计数解决所有问题。
也就是说,您可以在引用计数中实现一个调试功能,以跟踪持有引用的内容。然后,您可以在必要时分析此信息,并将其从发布版本中删除。(使用与使用 DEBUG 宏的目的相似的配置宏。)
您应该如何实现它取决于您的所有要求,但有两种主要方法可以做到这一点(简要概述差异):
知道什么在引用给定对象的基本问题通常很难解决,并且需要一些工作。比较:你能告诉我每个知道你的邮政地址或电话号码的人和企业吗?
引用计数的一个已知弱点是它在存在循环引用时不起作用,即(在最简单的情况下)一个对象引用另一个对象,而另一个对象又引用前一个对象。这听起来像一个非问题,但在数据结构中,例如具有对父节点的反向引用的二叉树,就可以了。
如果您没有在引用的(未释放的)对象中明确提供“反向”引用列表,我看不出有办法找出谁在引用它。
在以下建议中,我假设您不想修改您的源代码,或者如果是的话,只是一点点。
您当然可以遍历整个堆/freestore 并搜索未释放对象的内存地址,但如果它的地址出现,则不能保证它实际上是内存地址引用;它也可以是任何其他随机浮点数。但是,如果找到的值位于应用程序为一个对象分配的内存块内,那么它确实是指向另一个对象的指针的可能性会有所提高。
对这种方法的一个可能的改进是修改您使用的内存分配器——例如你的全局内存分配器operator new
——以便它保留所有已分配内存块及其大小的列表。(在此的完整实现中,operator delete
将删除已释放内存块的列表条目。)现在,在程序结束时,您知道在哪里搜索未释放对象的内存地址,因为您有您的程序实际使用的内存块列表。
老实说,上述建议对我来说听起来不太可靠;但也许定义一个自定义的全局operator new
,operator delete
这会在正确的方向上进行一些日志记录/跟踪以解决您的问题。
我假设您有一些具有 sayaddRef()
和release()
成员函数的类,并且当您需要增加和减少每个实例的引用计数时调用它们,并且导致问题的实例在堆上并使用原始指针引用。最简单的解决方法可能是将所有指向受控对象的指针替换为boost::shared_ptr
. 这非常容易做到,并且应该使您能够省去自己的引用计数——您可以让我提到的那些函数什么都不做。代码中所需的主要更改是传递或返回指针的函数的签名。其他需要更改的地方是初始化器列表(如果将指针初始化为 null)和 if() 语句(如果将指针与 null 进行比较)。更改指针的声明后,编译器会找到所有这些位置。
如果您不想使用shared_ptr
- 也许您想保留类固有的引用计数 - 您可以制作自己的简单智能指针来处理您的类。然后用它来控制你的类对象的生命周期。因此,例如,不是使用原始指针完成指针分配并且您“手动”调用addRef()
,您只需对包含addRef()
自动的智能指针类进行分配。
我认为如果不更改代码就不可能做某事。例如,通过代码更改,您可以记住增加引用计数的对象的指针,然后查看剩下的指针并在调试器中检查它。如果可能 - 存储更详细的信息,例如对象名称。
我已经为我的需要创建了一个。您可以将您的代码与此代码进行比较,看看缺少什么。它并不完美,但它应该适用于大多数情况。 http://sites.google.com/site/grayasm/autopointer
当我使用它时,我会:
util::autopointer<A> aptr=new A();
我从不这样做:
A* ptr = new A();
util::autopointer<A> aptr = ptr;
然后开始用 ptr 充实;这是不允许的。此外,我仅使用 aptr 来引用此对象。如果我错了,我现在有机会得到更正。:) 拜拜!