1

我使用boost::intrusive_ptr了很多来保持某些类的实例处于活动状态。在某些时候,我的程序希望所有boost::intrusive_ptrs 都已被删除,以便释放底层对象。

我最近似乎经常遇到的一个问题是,并非所有对象都boost::intrusive_ptr被删除并且所述对象仍然“活着”。这是一个错误。

我需要能够检测到这些指针在哪里。获取创建它们的源文件/行号列表就足够了(例如,如果调试输出告诉我在此处创建的指针仍然存在:

foo.m_ptr = foo::create<Foo>();

其中 m_ptr 是 aboost::intrusive_ptr<Foo>和/或boost::intrusive<Foo const>然后我可以弄清楚仍然没有被破坏的指针是Foo::m_ptr;)。

当然,在这种情况下,m_ptr之前已经创建了 - 甚至可能已分配。所以这里重要的不是跟踪的构造函数,boost::intrusive_ptr而是引用计数(最后)增加的地方。

上面的赋值函数如下所示:

intrusive_ptr<T>& operator=(intrusive_ptr<T> const& rhs)
{
    intrusive_ptr<T>(rhs).swap(*this);
    return *this;
}

因此,这个复制从 rhs 构造一个临时对象,增加 rhs 指向的对象的引用计数,然后将其交换,*this以便临时对象现在指向*this之前指向的对象,当前对象指向 rhs 指向的对象。最后,临时对象被破坏,导致*this指向的引用计数减少。

因此,从 中获取调用地址intrusive_ptr_add_ref需要展开堆栈两次(如果不是三次),可能取决于优化级别。

因此,我认为无法避免boost::intrusive_ptr用我自己的类替换,比如说utils::intrusive_ptr,它将实现上述功能,如下所示:

[[gnu::noinline]] intrusive_ptr<T>& operator=(intrusive_ptr<T> const& rhs)
{
    intrusive_ptr<T>(rhs, __builtin_return_address(0)).swap(*this);
    return *this;
}

它用于[[gnu::noinline]]确保__builtin_return_address(0)返回当前函数的返回地址。这个返回地址被传递给构造函数来注册它(返回地址可以很容易地转换为稍后的调用位置,即在打印仍然存在的指针列表时)。

同时需要删除存储的位置rhs。在我看来,确保这一点的最佳方法是将位置逐字存储在其intrusive_ptr本身中。然后,该位置也将被交换swap并因此在离开此范围时被破坏。

现有intrusive_ptrs 的注册可以通过它们的地址来完成:每个构造函数都有一个地址(其this指针),该地址将是唯一的,并且可以用作映射中的键。销毁后,此密钥将被再次删除。

这样,使用地图,就有一个所有现有intrusive_ptr对象的列表,这些对象存储自己的位置。

我的推理在这里正确吗?还是有其他方法可以做到这一点?

编辑:

我添加了一个新类,它基本上是 boost::intrusive_ptr 的副本,但去掉了任何 boost 并且只支持 c++17(及更高版本)。我还没有编译它,但这应该作为基础(?)。见https://github.com/CarloWood/ai-utils/blob/master/intrusive_ptr.h

4

0 回答 0