3.5 intrusive_ptr

intrusive_ptr也是一种引用计数型智能指针,但与之前介绍的scoped_ptr和shared_ptr不同,需要额外增加一些代码才能使用它。它的名字可能会给人造成误解,实际上它并不一定要“侵入”代理对象的内部修改数据。

如果现存代码已经有了引用计数机制管理的对象,那么intrusive_ptr是一个非常好的选择,它可以包装已有对象从而得到与shared_ptr类似的智能指针。

3.5.1 类摘要

intrusive_ptr的类摘要如下:

因为intrusive_ptr也是引用计数型指针,所以它的接口与shared_ptr很像,它同样支持比较操作,以及static_pointer_cast()、dynamic_pointer_cast()等转型操作,但它自己不直接管理引用计数,而是调用以下两个函数来间接管理引用计数:

intrusive_ptr的构造函数和reset()相比还多出一个add_ref参数,它表示是否增加引用计数,如果add_ref==false,那么它就相当于weak_ptr,只是简单地观察对象。

3.5.2 用法

假设我们已经有了一个自己实现引用计数的类counted_data:

为了让intrusive_ptr正常工作,我们需要实现它要求的两个回调函数由于ADL的原因,我们应该在类所在的名字空间或boost名字空间实现这两个函数。

需要注意的是在intrusive_ptr_release()函数中必须检查引用计数,因为intrusive_ptr不负责销毁实例,所以这个工作必须由我们自己完成。

实现intrusive_ptr_release()/intrusive_ptr_release()后,intrusive_ptr就可以管理counted_data了,示例代码如下:

可以看到,只需要编写少量代码,我们就可以复用既存的数据结构,获得一个与shared_ptr的用法几乎一样的智能指针,而且这样并没有增加多余的“开销”,这在某些对性能要求比较苛刻的场景里非常实用。

但大多数情况下,shared_ptr完全不必增加新代码,而且它的灵活性更高,使用intrusive_ptr前必须要确定它能够带来足够多的好处。

3.5.3 引用计数器

为了进一步简化实现引用计数的工作,intrusive_ptr在头文件<boost/smart_ptr/intrusive_ref_counter.hpp>里定义了一个辅助类intrusive_ref_counter:

intrusive_ref_counter内部定义了一个计数器变量m_ref_counter,使用模板参数配置策略类实现了引用计数的增减,默认的策略是线程安全的thread_safe_counter。intrusive_ref_counter需要被继承使用,这样其子类就会自动获得引用计数的能力,之前的counted_data可以简化为如下代码:

本书的12.1.4节使用atomic实现了一个更好的辅助类ref_count,读者可以参考。