intrusive_ptr
intrusive_ptr也是一种引用计数型智能指针,但与之前介绍的 scoped_ptr,shared_ptr 不同,需要额外增加一些的代码才能使用。它的名字可能会给人造成误解,实际上它并不一定要修改代理对象的内部数据。
如果现存代码已经有了引用计数机制管理的对象,那么 intrusive_ptr 是一个非常好的选择,可以包装已有对象从而得到与shared_ptr类似的智能指针。
类摘要
template<class T>
class intrusive_ptr
{
public:
typedef T element_type; //被代理的对象
intrusive_ptro(); //构造函数
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()等转型操作,但它自己不直接管理引用计数,而是调用下面两个函数来间接管理:
void intrusive_ptr_add_ref(T * p); //增加引用计数
void intrusive_ptr_release(T * p); //减少引用计数
intrusive_ptr的构造函数和reset()还多出一个add_ref参数,表示是否增加引用计数,如果add_refm=true,那么它就相当于weak_ptr,只是简单地观察对象。
用法
假设我们已经有了一个自己实现引用计数的类counted_data:
struct counted_data //自己实现引用计数
{
int m_count = 0; //引用计数
... //其他数据类型
};
为了让intrusive_ptr能够管理counted_data,我们需要实现它要求的两个回调函数:
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_release()和intrusive_ptr_release()后intrusive_ptr就可以管理counted_data了,示范代码如下:
int main()
{
typedef intrusive_ptr<counted_data> counted_ptr; //类型定义
counted_ptr p(new counted_data); //创建智能指针
assert(p); //bool转型
assert(p->m_count == 1); //operator->
counted_ptr p2(p); //指针拷贝构造
assert(p->m_count == 2); //引用计数增加
counted_ptr weak_p(p.get(), false); //弱引用
assert(weak_p->m_count == 2); //引用计数不增加
p2.reset(); //复位指针
assert(!p2); //p2不持有指针
assert(p->m_count == 1); //引用计数不增加
} //对象被正确析构
可以看到,只需要编写少量的代码,我们就可以复用既存的数据结构,获得一个与shared_ptr 用法几乎一样的智能指针,而且并没有增加多余的开销,这在某些对性能要求比较苛刻的场景里非常有用。
但大多数情况下shared_ptr 完全不必增加新代码,而且提供了更多的灵活性,使用intrusive_ptr前必须要确定它能够带来足够多的好处。
引用计数器
为了简化实现引用计数的工作, intrusive_ptr在头文件<boost/smart_ptr/intrusive_ref_counter.hpp>
里定义了一个辅助类intrusive_ref_counter,声明如下:
template<typename DerivedT,
typename CounterPolicyT = thread_safe_counter>
class intrusive_ref_counter
private:
typedef typenamie 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,使用模板参数CounterPolicyT配置策略类实现了计数的增减,缺省的策略是线程安全的thread_safe_counter。
intrusive_ref_counter需要被继承使用,这样子类就会自动获得引用计数的能力,之前的counted_data可以简化如下:
struct counted_data2 : public intrusive_ref_counter<counted_data2>
{
...
};
int main()
{
typedef intrusive_ptr<counted_data2> counted_ptr; //类型定义
counted_ptr p(new counted_data2); //创建智能指针
assert(p); //bool转型
assert(p->use_count() == 1); //operator->
} //对象被正确析构
代码示例
#include <iostream>
using namespace std;
#include <boost/smart_ptr.hpp>
using namespace boost;
//
struct counted_data
{
// ...
int m_count = 0;
~counted_data()
{
cout << "dtor" << endl;
}
};
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;
}
}
//
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
struct counted_data2 : public intrusive_ref_counter<counted_data2>
{
~counted_data2()
{
cout << "dtor2" << endl;
}
};
//
int main()
{
typedef intrusive_ptr<counted_data> counted_ptr;
counted_ptr p(new counted_data);
assert(p);
assert(p->m_count == 1);
counted_ptr p2(p);
assert(p->m_count == 2);
counted_ptr weak_p(p.get(), false);
assert(weak_p->m_count == 2);
p2.reset();
assert(!p2);
assert(p->m_count == 1);
{
typedef intrusive_ptr<counted_data2> counted_ptr;
counted_ptr p(new counted_data2);
assert(p);
assert(p->use_count() == 1);
}
}