intrusive_ptr 也是一种引用计数型智能指针,但与之前介绍的 scoped_ptr 和 shared_ptr 不同,需要额外增加一些代码才能使用它。它的名字可能会给人造成误解,实际上它并不一定要"侵入"代理对象的内部修改数据。
如果现在代码已经有了引用计数机制管理的对象,那么 intrusive_ptr 是一个非常好的选择,它可以包装已有对象从而得到与 shared_ptr 类似的智能指针。
intrusive_ptr 的类摘要如下:
cpp
template<class T> class intrusive_ptr {
public:
typedef T element_type; //被代理的对象
intrusive_ptr(); //构造函数
intrusive_ptr(T * p, bool add_ref = true);
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
~intrusive_ptr();
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
void reset(); //重置指针
void reset(T * r);
void reset(T * r, bool add_ref);
T & operator*() const; //操作符重载
T * operator->() const;
explicit operator bool() const;
T * get() const;
T * detach();
void swap(intrusive_ptr & b);
};
因为 intrusive_ptr 也是引用计数型指针,所以它的接口与 shared_ptr 很像,它同样支持比较操作,以及 static_pointer_cast() 、dynamic_pointer_cast() 等转型操作,但它自己不直接管理引用计数,而是调用以下两个函数来间接管理引用计数:
cpp
void intrusive_ptr_add_ref(T * p); //增加引用计数
void intrusive_ptr_release(T * p); //减少引用计数
intrusive_ptr 的构造函数和 reset() 相比还多出一个 add_ref 参数,它表示是否增加引用计数,如果 add_ref==false ,那么它就相当于 weak_ptr ,只是简单地观察对象。
假设我们已经有了一个自己实现引用计数的类 counted_data:
cpp
struct counted_data //自己实现引用计数
{
int m_count = 0; //引用计数
... //其他数据成员
};
为了让 intrusive_ptr 正常工作,我们需要实现它要求的两个回调函数:
cpp
void intrusive_ptr_add_ref(counted_data* p) //增加引用计数
{
++p->m_count;
}
void intrusive_ptr_release(counted_data* p) //减少引用计数
{
if(--p->m_count == 0)
{
delete p; //引用计数为 0 则删除指针
}
}
需要注意的是在 intrusive_ptr_release() 函数中必须检查引用计数,因为 intrusive_ptr 不负责销毁实例,所以这个工作必须由我们自己完成。
实现 intrusive_ptr_add_ref()/intrusive_ptr_release() 后,intrusive_ptr 就可以管理 counted_data 了,示例代码如下:
cpp
#include <iostream>
#include <string>
#include <Windows.h>
#include <boost/smart_ptr.hpp>
using namespace std;
struct counted_data //自己实现引用计数
{
int m_count = 0; //引用计数
};
void intrusive_ptr_add_ref(counted_data* p) //增加引用计数
{
++p->m_count;
}
void intrusive_ptr_release(counted_data* p) //减少引用计数
{
if (--p->m_count == 0)
{
delete p; //引用计数为 0 则删除指针
}
}
int main()
{
cout << "Start" << endl;
typedef boost::intrusive_ptr<counted_data> counted_ptr; //类型定义
counted_ptr p(new counted_data); //创建智能指针
assert(p); //bool 转型
cout << "创建对象p后计数:" << p->m_count << endl; //operator->
counted_ptr p2(p); //指针拷贝构造
cout << "拷贝构造对象p2后p的计数:" << p->m_count << endl; //引用计数增加
counted_ptr weak_p(p.get(), false); //弱引用
cout << "弱拷贝构造对象weak_p后p的计数:" << weak_p->m_count << endl; //引用计数不增加
p2.reset(); //复位指针
assert(!p2); //p2 不持有指针
cout << "释放拷贝构造对象p2后p的计数:" << p->m_count << endl; //引用计数减少
cout << "End" << endl;
system("pause");
return 0;
}
运行结果:
cpp
Start
创建对象p后计数:1
拷贝构造对象p2后p的计数:2
弱拷贝构造对象weak_p后p的计数:2
释放拷贝构造对象p2后p的计数:1
End
可以看到,只需要编写少量代码,我们就可以复用既存的数据结构,获得一个与shared_ptr的用法几乎一样的智能指针,而且这样并没有增加多余的"开销",这在某些对性能要求比较苛刻的场景里非常实用。
但大多数情况下,shared_ptr完全不必增加新代码,而且它的灵活性更高,使用intrusive_ptr前必须要确定它能够带来足够多的好处。
为了进一步简化实现引用计数的工作,intrusive_ptr 在头文件<boost/smart_prt/intrusive_ref_counter.hpp> 里定义了一个辅助类 intrusive_ref_counter :
cpp
template<typename Derived, typename CounterPolicyT = thread_safe_counter>
class intrusive_ref_counter {
private:
typedef typename CounterPolicyT::type counter_type;
mutable counter_type m_ref_counter;
public:
intrusive_ref_counter();
unsigned int use_count() const;
protected:
~intrusive_ref_counter() = default;
friend void intrusive_ptr_add_ref(const intrusive_ref_counter* p);
friend void intrusive_ptr_release(const intrusive_ref_counter* p);
};
intrusive_ref_counter 内部定义了一个计数器变量 m_ref_counter ,使用模板参数配置策略类实现了引用计数的增减,默认的策略是线程安全的 thread_safe_counter 。
intrusive_ref_counter 需要被继承使用,这样其子类就会自动获得引用计数的能力,之前的 counted_data 可以简化为如下代码:
cpp
#include <iostream>
#include <string>
#include <Windows.h>
#include <boost/smart_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
using namespace std;
struct counted_data :public boost::intrusive_ref_counter<counted_data> //自己实现引用计数
{
int m_count = 0; //引用计数
};
int main()
{
cout << "Start" << endl;
typedef boost::intrusive_ptr<counted_data> counted_ptr; //类型定义
counted_ptr p(new counted_data); //创建智能指针
assert(p); //bool 转型
cout << "创建对象p后计数:" << p->use_count() << endl; //operator->
counted_ptr p2(p); //指针拷贝构造
cout << "拷贝构造对象p2后p的计数:" << p->use_count() << endl; //引用计数增加
counted_ptr weak_p(p.get(), false); //弱引用
cout << "弱拷贝构造对象weak_p后p的计数:" << weak_p->use_count() << endl; //引用计数不增加
p2.reset(); //复位指针
assert(!p2); //p2 不持有指针
cout << "释放拷贝构造对象p2后p的计数:" << p->use_count() << endl; //引用计数减少
cout << "End" << endl;
system("pause");
return 0;
}
代码运行结果:
cpp
Start
创建对象p后计数:1
拷贝构造对象p2后p的计数:2
弱拷贝构造对象weak_p后p的计数:2
释放拷贝构造对象p2后p的计数:1
End