c++中智能指针的作用是非常大的,它不仅能解决很多裸指针带来的内存相关问题,还能用来管理资源(RAII)。
c++中的智能指针有很多种,其中以shared_ptr和weak_ptr最为重要(个人观点),掌握了这两个智能指针的原理,其它的就很好理解了。
shared_ptr是典型的引用计数型智能指针,相信很多c++程序员都写过功能类似的智能指针类。很多时候用这个指针就足够了。
但是有些情况下,shared_ptr可能产生循环引用, 比如Observer模式中,或者父子关系的对象等。为了解决循环引用而造成的内存泄露,引入了weak_ptr。
shared_ptr是strong reference, weak_ptr是weak reference。shared_ptr保证所持有的raw指针是有效的,而weak_ptr则不能保证所持有的指针是否还指向一个有效的对象。
在需要访问所持有的指针的时候,调用weak_ptr的expired或者lock就能判断出是否能得到对应的shared_ptr。
本文的目的不是为了介绍shared_ptr和weak_ptr的使用方法,而是为了介绍它们的实现原理,更主要的是为了说明weak_ptr是如何知道是否还有shared_ptr对象存在的。
还有一些关于它们使用习惯或注意点的说明。
一、shared_ptr
- ref_count / control block (控制块)
在典型的实现中,std::shared_ptr 只保存两个指针:
- 指向被管理对象的指针
- 指向控制块(control block)的指针
控制块是一个动态分配的对象,其中包含:
- 指向被管理对象的指针或被管理对象本身
- 删除器
- 分配器(allocator)
- 拥有被管理对象的
shared_ptr
的数量 - 引用被管理对象的
weak_ptr
的数量
通过 std::make_shared 和 std::allocate_shared 创建 shared_ptr
时,控制块将被管理对象本身作为其数据成员;而通过构造函数创建 shared_ptr
时则保存指针。
class _Ref_count_base { // common code for reference countingprivate:virtual void _Destroy () = 0 ;virtual void _Delete_this () = 0 ;
long _Uses ;long _Weaks ;
protected:_Ref_count_base () : _Uses ( 1 ), _Weaks ( 1 ) { // construct}...};template < class _Ty > class _Ref_count : public _Ref_count_base {};template < class _Ty , class _Dx > class _Ref_count_del : public _Ref_count_base {};template < class _Ty ,class _Dx ,class _Alloc >class _Ref_count_del_alloc: public _Ref_count_base {};
_Ty * _Ptr ;_Ref_count_base * _Rep ;
explicit shared_ptr ( _Ux * _Px ){ // construct shared_ptr object that owns _Px_Resetp ( _Px );}private:template < class _Ux >void _Resetp ( _Ux * _Px ){ // release, take ownership of _Px_TRY_BEGIN // allocate control block and reset_Resetp0 ( _Px , new _Ref_count < _Ux > ( _Px ));_CATCH_ALL // allocation failed, delete resourcedelete _Px ;_RERAISE ;_CATCH_END}public:template < class _Ux >void _Resetp0 ( _Ux * _Px , _Ref_count_base * _Rx ){ // release resource and take ownership of _Pxthis -> _Reset0 ( _Px , _Rx );_Enable_shared(_Px, _Rx);}};void _Reset0 ( _Ty * _Other_ptr , _Ref_count_base * _Other_rep ){ // release resource and take new resourceif ( _Rep != 0 )_Rep -> _Decref ();_Rep = _Other_rep ;_Ptr = _Other_ptr ;}};
template < class _Ty > class weak_ptr : public _Ptr_base < _Ty > {...template < class _Ty2 >weak_ptr ( const shared_ptr < _Ty2 >& _Other ,typename enable_if < is_convertible < _Ty2 * , _Ty *>:: value ,void *>:: type * = 0 ){ // construct weak_ptr object for resource owned by _Otherthis -> _Resetw ( _Other );}
};
template < class _Ty > class _Ref_count_obj : public _Ref_count_base {};// TEMPLATE FUNCTION make_sharedtemplate<class _Ty _C_CLASS_ARG0> inlineshared_ptr<_Ty> make_shared(_ARG0_A0_REFREF){ // make a shared_ptr_Ref_count_obj<_Ty> * _Rx = new _Ref_count_obj<_Ty>(_A0_A1_FWD);shared_ptr<_Ty> _Ret;_Ret._Resetp0(_Rx->_Getptr(), _Rx);return (_Ret);}上面基本上把shared_ptr实现相关的类都列出来了。
从代码中可以看到, shared_ptr和weak_ptr都继承自_Ptr_base, _Ptr_base中有两个指针:_Ptr是指向被管理的对象的,_Rep指向控制块。_Ty * _Ptr ;_Ref_count_base * _Rep ;控制块的基类是_Ref_count_base,这是一个纯虚类。它定义了两个纯虚函数和两个成员变量:并且定义了以下函数: _Incref/_Incwref/_Decref/_Decwref/_Use_count/_Expiredvirtual void _Destroy () = 0 ;virtual void _Delete_this () = 0 ;
long _Uses ;long _Weaks ;
这些主要负责操作这两个成员变量,当_Used==0时,调用 _Destroy把被管理对象释放掉,当_Weaks==0时,调用_Delete_this把控制块释放掉。
- shared_ptr<T>(raw_pointer)
shared_ptr<int> pi(new int(100));
weak_ptr<int> wpi(pi);
weak_ptr<int> wpi2 = pi, wpi3 = pi;
|
- make_shared<T>(args)
shared_ptr<float> pf = make_shared<float>(3.14);
weak_ptr<float> wpf = pf;
weak_ptr<float> wpf2 = pf, wpf3 = pf, wpf4 = pf;
- 使用注意事项:
考虑下面这个情况:
int *a = new int;
std::shared_ptr<int> p1(a);
std::shared_ptr<int> p2(a);
二、weak_ptr
weak_ptr的实现和shared_ptr比较起来相对简单,阅读一下代码就能理解。
这里主要说一下_Weaks的初始值的问题。
从上面的调试结果可以看到,pf显示有1个strong ref,4 weak refs,但它内部的控制块是_Uses = 1, _Weaks = 5。
这是因为_Weaks的初始值是1,而不是0。_Weaks = (weak指针的个数) + (_Uses>0 ? 1 : 0)
根据视频里的讲解,这主要是为了性能优化。
试想如果_Weaks初始化为0,在多线程的情况下,如果_Weaks被更新了,它需要判断_Uses的值来决定是否要delete 控制块,这样每次都要判断,影响效率。
而初始化为1,当_Uses>0时,_Weaks比真正的weak指针的个数多1。当_Uses=0的时候,会将_Weaks减1,这样_Weaks就和weak指针的个数对应了。这样只要_Weaks不为0,就不用delete控制块。
void _Decref (){ // decrement use countif ( _MT_DECR ( _Mtx , _Uses ) == 0 ){ // destroy managed resource, decrement weak reference count_Destroy ();_Decwref ();}}
void _Decwref (){ // decrement weak reference countif ( _MT_DECR ( _Mtx , _Weaks ) == 0 )_Delete_this ();}
三、enable_shared_from_this
- 使用场景
- 错误实现
class A: public std::enable_shared_from_this<A>
{
public:
A() { cout << "A constructor\n"; }
~A() { cout << "A destructor\n"; }
shared_ptr<A> bad_get_me() {
return shared_ptr<A>(this);
}
shared_ptr<A> good_get_me() {
return shared_from_this();
}
};
std::shared_ptr<A> pa = make_shared<A>();
std::shared_ptr<A> pb = pa->bad_get_me();
如果调用bad_get_me,则pb和pa是两个不同的智能指针,但他们指向同一个原始指针,这样会造成同一个指针释放两次,从而crash。
- 正确实现
std::shared_ptr<A> pb = pa->good_get_me();
如果调用good_get_me,则pb和pa是共享同一个control block,此时pb相对于是调用拷贝构造函数得到的,这也是shared_ptr的正确用法。
- 原理
// TEMPLATE CLASS enable_shared_from_thistemplate < class _Ty > class enable_shared_from_this{ // provide member functions that create shared_ptr to thispublic:typedef _Ty _EStype ;
shared_ptr < _Ty > shared_from_this (){ // return shared_ptrreturn ( shared_ptr < _Ty > ( _Wptr ));}
shared_ptr < const _Ty > shared_from_this () const{ // return shared_ptrreturn ( shared_ptr < const _Ty > ( _Wptr ));}
protected:enable_shared_from_this (){ // construct (do nothing)}
enable_shared_from_this ( const enable_shared_from_this & ){ // construct (do nothing)}
enable_shared_from_this & operator = ( const enable_shared_from_this & ){ // assign (do nothing)return ( * this );}
~ enable_shared_from_this (){ // destroy (do nothing)}
private:template < class _Ty1 ,class _Ty2 >friend void _Do_enable (_Ty1 * ,enable_shared_from_this < _Ty2 >* ,_Ref_count_base * );
mutable weak_ptr < _Ty > _Wptr ;};
template < class _Ty1 ,class _Ty2 >inline void _Do_enable (_Ty1 * _Ptr ,enable_shared_from_this < _Ty2 > * _Es ,_Ref_count_base * _Refptr ){ // reset internal weak pointer_Es -> _Wptr . _Resetw ( _Ptr , _Refptr );}
template < class _Ty >inline void _Enable_shared ( _Ty * _Ptr , _Ref_count_base * _Refptr ,typename _Ty :: _EStype * = 0 ){ // reset internal weak pointerif ( _Ptr )_Do_enable ( _Ptr , ( enable_shared_from_this < typename _Ty :: _EStype >* ) _Ptr , _Refptr );}
inline void _Enable_shared ( const volatile void * , const volatile void * ){ // not derived from enable_shared_from_this; do nothing}
参考链接:
- http://zh.cppreference.com/w/cpp/memory/shared_ptr
- http://zh.cppreference.com/w/cpp/memory/enable_shared_from_this
- http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Advanced-STL/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-1-of-n
- https://msdn.microsoft.com/en-us/library/hh279674.aspx
- https://msdn.microsoft.com/en-us/library/ee410595.aspx
- https://msdn.microsoft.com/en-us/library/hh279669.aspx
- http://www.cnblogs.com/heleifz/p/shared-principle-application.html