shared_ptr是一个最像指针的“智能指针”,是boost.smart_ptr库中最有价值、最重要的组成部分,也是最有用的,Boost库的许多组件–甚至还包括其他一些领域的智能指针都使用了shared_ptr。抱歉,我实在想不出什么更恰当的词汇来形容它在软件开发中的重要性。再强调一遍,shared_ptr非常有价值、非常重要、非常有用。——《Boost程序库完全开发指南》
shared_ptr与scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。shared_ptr也可以安全地放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。
在C++历史上曾经出现过无数的引用计数型智能指针实现,但没有一个比得上boost::shared_ptr,在过去、现在和将来,它都是最好的。
shared_ptr的思想是,每当另一个智能指针指向该空间时,引用计数++,每当有智能指针更改指向时,引用计数–。这就意味着,有几个智能指针指向该空间,引用计数就是几,当引用计数大于2时,证明有2个以上的智能指针指向该空间,那么析构时,把智能指针赋空,不需释放空间。当且仅当引用计数为1时,即只有一个指针指向该空间,析构时才将指针赋空并释放空间,证明没有任何代码需要管理该空间,就把它释放掉。
引用计数的引入,解决auto_ptr和scoped_ptr不能解决的问题,它使得空间的释放更具有安全性。
shared_ptr的架构由四部分组成。
分别是class shared_ptr:
template<class _Ty>
class shared_ptr
{
typedef shared_ptr<_Ty> this_type;
public:
shared_ptr():px(0){}
shared_ptr(const shared_ptr<_Ty> &r):px(r.px),pn(r.pn){}
shared_ptr(_Ty *p):px(p),pn(p){}
shared_ptr<_Ty>& operator=(const shared_ptr<_Ty> &r)
{
if(this != &r)
{
this_type(r).swap(*this);
}
return *this;
}
~shared_ptr()
public:
_Ty& operator*()const
{return *px;}
_Ty* operator->()const
{return px;}
public:
long use_count()const
{
return pn.use_count();
}
public:
void swap(shared_ptr<_Ty> & b) // never throw
{
_Ty * tmp = b.px;
b.px = px;
px = tmp;
}
private:
_Ty *px;
shared_count pn;
};
引用计数 class shared_count:
class shared_count
{
public:
shared_count():pi_(0){}
template<class _Ty>
shared_count(_Ty *p):pi_(new sp_counted_impl_xxx<_Ty>(p)){}
shared_count(const shared_count &r):pi_(r.pi_)
{
pi_->add_ref_copy();
}
~shared_count()
{
if(pi_ != 0)
pi_->release();
}
public:
long use_count()const
{
return pi_ ? pi_->use_count() : 0;
}
private:
sp_counted_base *pi_;
};
继承基类 class sp_counted_base
class sp_counted_base
{
public:
sp_counted_base() : use_count_(1){}
virtual ~sp_counted_base(){}
public:
virtual void dispose() = 0;
public:
void add_ref_copy()
{++use_count_;}
long use_count()const
{
return use_count_;
}
void release()
{
if(--use_count_ == 0)
{
dispose();
delete this;
}
}
private:
long use_count_;
};
继承子类class sp_counted_impl_xxx:
class sp_counted_impl_xxx : public sp_counted_base
{
public:
sp_counted_impl_xxx(_Ty *p) : px_(p){}
~sp_counted_impl_xxx(){}
public:
void dispose()
{
delete px_;
}
private:
_Ty *px_;
};
shared_ptr的构架如图所示:
当将指针传给shared_ptr时,class shared_ptr构造对象,px指向该指针指向的空间,而pn是shared_count类的,所以先构造shared_count的对象,shared_count(Ty *p):pi(new sp_counted_impl_xxx<_Ty>(p)),构造对象时p是子类sp_counted_impl_xxx类型的,所以要先构造父类sp_counted_base的对象。
所以shared_ptr的构造顺序是:
sp_counted_base -> sp_counted_impl_xxx -> shared_count -> shared_ptr
这样设计shared_ptr是有原因的,将各个数据存在不同类中,使得程序更有弹性。
shared_ptr中px指针接受“裸指针”进行管理,而引用计数use_count_存放在父类中,不同的子类都可以继承引用计数的方法,而子类又引入了指针*px_,px_和px是相同的指向,加入这个指针是为了操作方便。
但需要注意的是,这两个指针指向相同,要防止二次释放。px只管接受指针,当引用计数use_count_为1时,px_负责释放空间,同时要将px赋空。(px和px_都属于shared_ptr,所以两者虽指向相同,当count不++)
操作函数
shared_ptr与scoped_ptr同样是管理new动态分配对象的智能指针,因此功能上有很多的相似之处:它们都重载了*和->操作符以模仿原始指针的行为,提供隐式bool类型转换以判断指针的有效性,get()可以得到原始指针,并且没有提供指针算术操作。
shared_ptr<int>spi(new int) //一个int的shared_ptr
assert(spi); //在bool语境中隐式转换为bool值
*spi = 100; //使用解引用操作符*
shared_ptr<string> sps(new string(" "));//一个string的shared_ptr
assert(sps->size() == 5); //使用箭头操作符->
但shared_ptr的名字表明它与scoped_ptr的主要不同:
shared_ptr是可以被安全共享的——shared_ptr是一个”全功能”的类,有着正常的拷贝,赋值语义,也可以进行shared_ptr间的比较,是最智能的指针!
线程安全性
shared_ptr 本身不是 100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。根据文档,shared_ptr 的线程安全级别和内建类型、标准库容器、string 一样,即:
- 一个 shared_ptr 实体可被多个线程同时读取;
- 两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
- 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。
应用于标准容器
有两种方式可以将shared_ptr应用于标准容器(或者容器适配器等其他容器)。
一种用法是将容器作为shared_ptr管理的对象,如shared_ptr < list < T > >
,使容器可以被安全地共享,用法与普通指针没有区别。
另一种用法是将shared_ptr作为容器的元素,如
vector < shared_ptr < T > >
,因为shared_ptr支持拷贝语义和比较操作,符合标准容器对元素的要求,所以可以实现在容器中安全地容纳元素的指针而不是拷贝。
用法
shared_ptr的智能使得其行为最接近原始指针,因此它比auto_ptr和scoped_ptr的应用更广。几乎是100%可以在任何new出现的地方接受new的动态分配结果,然后被任意的使用,从而完全消灭delete的使用和内存泄漏。
shared_ptr 用法实例一:
shared_ptr<int> sp(new int(10)); //一个指向整数的shared_ptr
assert(sp.unique()); //现在shared_ptr是指针的唯一持有者
shared_ptr<int> sp2 = sp; //第二个shared_ptr,拷贝构造函数
assert(sp == sp2 && sp.use_count() == 2); //两个shared_ptr相等,指向同一个对象,引用计数为2
*sp2 = 100; //使用解引用操作符修改被指对象
assert(*sp == 100); //另一个shared_ptr也同时被修改
sp.reset(); //停止shared_ptr的使用
assert(!sp); //sp不再持有任何指针(空指针)
shared_ptr 用法实例二:
class shared //一个拥有shared_ptr的类
{
private:
shared_ptr<int> p; //shared_ptr成员变量
public:
shared(shared_ptr<int> p_):p(p_){} //构造函数初始化shared_ptr
void print() //输出shared_ptr的引用计数和指向的值
{
cout << "count:" << p.use_count()
<< "v =" <<*p << endl;
}
};
void print_func(shared_ptr<int> p) //使用shared_ptr作为函数参数
{
cout << "count:" << p.use_count() //同样输出shared_ptr的引用计数和指向的值
<< " v=" <<*p << endl; }
int main()
{
shared_ptr<int> p(new int(100));
shared s1(p), s2(p); //构造两个自定义类
s1.print();
s2.print();
*p = 20; //修改shared_ptr所指的值
print_func(p);
s1.print();
}
shraed_ptr涉及的问题还远远没有结束,shared_ptr为了封装new还会引入工厂函数系列的问题;sahred_ptr应用于桥接模式和pimpl的问题;以及为了配合shared_ptr的使用 引入weak_ptr(弱指针)的问题;还有intrusive_ptr(侵入式指针)与shared_ptr的比较问题,都是值得大家去深入学习的。
当然,今天就不一一详解了,如果有时间我会继续整理这方面的资料。