intrusive_ptr
直接进入正题吧
在boost库中经常会用到shared_ptr这个智能指针,在正常情况下如下:
A *p = new A();
boost::shared_ptr(A) pa(p);
boost::shared_ptr(A) pa0(pa);
这样使用是毫无问题的,但是如果是这样使用如下:
A *p = new A();
boost::shared_ptr(A) pa(p);
boost::shared_ptr(A) pa0(p);
则程序的结果是毫无疑问的会挂掉,下面就来分析一下原因。
在正常情况下boost::shared_ptr(A) pa(p)这句执行完之后pa里面指针的引用计数是1,
在执行boost::shared_ptr(A) pa0(pa)后,pa中的引用计数为2,pa0中的引用计数也为2,所在在两者都释放后(不管哪一个先释放或后释放,每释放一个引用计数就减1)最后的引用计数就会减少到0,导致p所指向的内存释放。
而在下面的一种情况当中在执行boost::shared_ptr(A) pa0(p)之后,pa的引用计数不会变,依然是1,pa0中的引用计数也是1,所以当其中的一个释放时,对应的p指向的内存就会被释放掉,当另外一个对象释放时,就会释放一个已经释放掉的内存。
遇到这种问题怎么办?在boost中也引入了intrusive_ptr指针,只不过这个指针用起来稍微有点麻烦。先来讲讲intrrusive_ptr指针,当我们在内存中new一个对象时,在这个对象的内存区开辟一 个变量来专门记录对该块内存的引用计数,这个引用计数就是专门用来记录当前还有多少个指针指向了这块内存。如下图:
下面是boost中intrusive_ptr中部分代码
#endif
: px( rhs.get() )
{
if( px != 0 ) intrusive_ptr_add_ref( px );
}
#endif
intrusive_ptr(intrusive_ptr const & rhs): px( rhs.px )
{
if( px != 0 ) intrusive_ptr_add_ref( px );
}
~intrusive_ptr()
{
if( px != 0 ) intrusive_ptr_release( px );
}
里面比较重要的两个函数是intrusive_ptr_add_ref和intrusive_ptr_release,这两个函数是专门用来增加和减小内存对象中引用计数的。所以可以将所有需要使用intrusive_ptr的对象都继承一个基类,专门用来处理引用计数,类的其他部分和普通类一样使用即可。
共同的基类如下:
template<class T>
class intrusive_ptr_base {
public:
/**
* 缺省构造函数
*/
intrusive_ptr_base(): ref_count(0) {
std::cout << " Default constructor " << std::endl;
}
/**
* 不允许拷贝构造,只能使用intrusive_ptr来构造另一个intrusive_ptr
*/
intrusive_ptr_base(intrusive_ptr_base<T> const&): ref_count(0) {
std::cout << " Copy constructor..." << std::endl;
}
/**
* 不允许进行赋值操作
*/
intrusive_ptr_base& operator=(intrusive_ptr_base const& rhs) {
std::cout << " Assignment operator..." << std::endl;
return *this;
}
/**
* 递增引用计数(放到基类中以便compiler能找到,否则需要放到boost名字空间中)
*/
friend void intrusive_ptr_add_ref(intrusive_ptr_base<T> const* s) {
std::cout << " intrusive_ptr_add_ref..." << std::endl;
assert(s->ref_count >= 0);
assert(s != 0);
++s->ref_count;
}
/**
* 递减引用计数
*/
friend void intrusive_ptr_release(intrusive_ptr_base<T> const* s) {
std::cout << " intrusive_ptr_release..." << std::endl;
assert(s->ref_count > 0);
assert(s != 0);
if (--s->ref_count == 0)
boost::checked_delete(static_cast<T const*>(s)); //s的实际类型就是T,intrusive_ptr_base<T>为基类
}
/**
* 类似于shared_from_this()函数
*/
boost::intrusive_ptr<T> self() {
return boost::intrusive_ptr<T>((T*)this);
}
boost::intrusive_ptr<const T> self() const {
return boost::intrusive_ptr<const T>((T const*)this);
}
int refcount() const {
return (int)long(ref_count);
}
private:
///should be modifiable even from const intrusive_ptr objects
mutable boost::detail::atomic_count ref_count;
};
下面是对intrusive_ptr的一个简单测试,代码如下:
#include "intrusive_ptr_base.hpp"
class Connection : public intrusive_ptr_base< Connection >
{
public:
Connection(int id, std::string tag)
: connection_id( id )
, connection_tag( tag ) {}
Connection(const Connection& rhs)
: connection_id( rhs.connection_id )
, connection_tag( rhs.connection_tag) {}
const Connection operator=( const Connection& rhs)
{
if(this != &rhs)
{
connection_id = rhs.connection_id;
connection_tag = rhs.connection_tag;
}
return *this;
}
private:
int connection_id;
std::string connection_tag;
};
void testintrusive()
{
std::cout << "Create an intrusive ptr" << std::endl;
boost::intrusive_ptr< Connection > con0 (new Connection(4, "sss") );
std::cout << "Create an intrusive ptr. Refcount = " << con0->refcount() << std::endl;
boost::intrusive_ptr< Connection > con1 (con0);
std::cout << "Create an intrusive ptr. Refcount = " << con1->refcount() << std::endl;
boost::intrusive_ptr< Connection > con2 = con0;
std::cout << "Create an intrusive ptr. Refcount = " << con2->refcount() << std::endl;
std::cout << "Create an intrusive ptr. Refcount = " << con2->refcount() << std::endl;
std::cout << "Destroy an intrusive ptr" << std::endl;
}
下图是程序运行结果:
总结:1、instrusive_ptr相比shared_ptr使用起来要稍微麻烦一些,并且功能要强大一些,shared_ptr可以做到的intrusive_ptr都可以做到
2、两个指针的内存模型不一样,shared_ptr的引用计数在shared_ptr对象内存当中,而intrusive_ptr的引用计数则在类型对象的内存当中