类结构框架
下面这张图来自于网络,已不知原作者。这张图表明了shared_ptr类结构图
_Ref_count_base是在shared_ptr创建的时候new出来的。_Ref_count_base有3个子类,分别对应shared_ptr构造时的3种形式。在shared_ptr的实现中,会根据调用的shared_ptr构造函数的不同,使用不同类型的_Ref_count_base创建。_Ref_count_base中保存了两个引用计数:
_Uses:用于shared_ptr,表示该_Ref_count_base被几个shared_ptr引用了。当_Uses等于0时,会释放_Ref_count_base中保存的对象(也就是用户传入的指针)。
_Weaks:用于weak_ptr,表示该_Ref_count_base被几个weak_ptr引用了。当_ Weaks等于0时,会释放_Ref_count_base自身。
有了这两个引用计数,就能实现智能指针管理资源基本功能以及野指针检查的功能。
// CLASS _Ref_count_base
class _Ref_count_base
{ // common code for reference counting
private:
virtual void _Destroy() = 0;
virtual void _Delete_this() = 0;
long _Uses;
long _Weaks;
protected:
_Ref_count_base()
: _Uses(1), _Weaks(1)
{ // construct
}
public:
virtual ~_Ref_count_base()
{ // ensure that derived classes can be destroyed properly
}
bool _Incref_nz()
{ // increment use count if not zero, return true if successful
for (; ; )
{ // loop until state is known
long _Count = (volatile long&)_Uses;
if (_Count == 0)
return (false);
if (_MT_CMPX(_Uses, _Count + 1, _Count) == _Count)
return (true);
}
}
void _Incref()
{ // increment use count
_MT_INCR(_Mtx, _Uses);
}
void _Incwref()
{ // increment weak reference count
_MT_INCR(_Mtx, _Weaks);
}
void _Decref()
{ // decrement use count
if (_MT_DECR(_Mtx, _Uses) == 0)
{ // destroy managed resource, decrement weak reference count
_Destroy();
_Decwref();
}
}
void _Decwref()
{ // decrement weak reference count
if (_MT_DECR(_Mtx, _Weaks) == 0)
_Delete_this();
}
long _Use_count() const
{ // return use count
return (_Uses);
}
bool _Expired() const
{ // return true if _Uses == 0
return (_Uses == 0);
}
virtual void *_Get_deleter(const _XSTD2 type_info&) const
{ // return address of deleter object
return (0);
}
};
// TEMPLATE CLASS _Ref_count, _Ref_count_del, _Ref_count_del_alloc
template<class _Ty>
class _Ref_count
: public _Ref_count_base
{ // handle reference counting for object without deleter
public:
_Ref_count(_Ty *_Px)
: _Ref_count_base(), _Ptr(_Px)
{ // construct
}
private:
virtual void _Destroy()
{ // destroy managed resource
delete _Ptr;
}
virtual void _Delete_this()
{ // destroy self
delete this;
}
_Ty * _Ptr;
};
原理
记得早些年间看《C++ Primer Plus》里面讲到类的静态(static)成员变量的时候,举得例子大概就是说:有一个类MyClass,此类中有一个static变量m_nCount,然后再类的构造函数中将m_nCount+1,再析构函数中将m_nCount-1,那么所有MyClass的对象数量就可以使用m_nCount来表示。我想这里的思想与shared_ptr有异曲同工之妙。在所有的shared_ptr中,都共享同一个_Ref_count_base,在_Ref_count_base中记录着一个_Uses,用以代表shared_ptr的个数。每构造一个shared_ptr,_Uses +1,析构一个shared_ptr,_Uses-1。当然除了第一个shared_ptr是直接引用原生指针之外,其他的shared_ptr必须是由之前的shared_ptr赋值或复制得来的。
从构造说起
有如下一行代码
shared_ptr<int> pa(new int(11));
template<class _Ux>
explicit shared_ptr(_Ux *_Px)
{ // construct shared_ptr object that owns _Px
_Resetp(_Px);
}
下面代码有一处代码 :new _Ref_count<_Ux>(_Px),当第一个对某指针A引用的shared_ptr创建的时候,会在堆上生成_Ref_coun对象。
template<class _Ux>
void shared_ptr::_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 resource
delete _Px;
_RERAISE;
_CATCH_END
}
_Ref_count::_Ref_count(_Ty *_Px)
: _Ref_count_base(), _Ptr(_Px)
{ // construct
}
所以对指针A引用的shared_ptr都共用一个_Ref_count,当然在第一次创建_Ref_count的时候,_Uses肯定设为1,毕竟已经有一个shared_ptr引用指针A了。
_Ref_count_base::_Ref_count_base()
: _Uses(1), _Weaks(1)
{ // construct
}
template<class _Ux>
void shared_ptr::_Resetp0(_Ux *_Px, _Ref_count_base *_Rx)
{ // release resource and take ownership of _Px
this->_Reset0(_Px, _Rx);
_Enable_shared(_Px, _Rx);
}
void _Ptr_base::_Reset0(_Ty *_Other_ptr, _Ref_count_base *_Other_rep)
{ // release resource and take new resource
if (_Rep != 0)
_Rep->_Decref();
_Rep = _Other_rep;//设置_Rep
_Ptr = _Other_ptr;//设置_Ptr
}
多个shared_ptr共享同一个变量
shared_ptr<int> pb = pa;
shared_ptr(const _Myt& _Other)
{ // construct shared_ptr object that owns same resource as _Other
this->_Reset(_Other);
}
template<class _Ty2>
void _Ptr_base::_Reset(const _Ptr_base<_Ty2>& _Other)
{ // release resource and take ownership of _Other._Ptr
_Reset(_Other._Ptr, _Other._Rep, false);
}
void _Ptr_base::_Reset(_Ty *_Other_ptr, _Ref_count_base *_Other_rep, bool _Throw)
{ // take _Other_ptr through _Other_rep from weak_ptr if not expired
// otherwise, leave in default state if !_Throw,
// otherwise throw exception
//将我们被复制的那个_Other_rep的_Uses+1,然后再将_Other_rep与它的所引用指针复制到this
if (_Other_rep && _Other_rep->_Incref_nz())
_Reset0(_Other_ptr, _Other_rep);
else if (_Throw)
_THROW_NCEE(bad_weak_ptr, 0);
}
#define _MT_CMPX(x, y, z) _InterlockedCompareExchange(&x, y, z)
bool _Incref_nz()
{ // increment use count if not zero, return true if successful
for (; ; )
{ // loop until state is known
long _Count = (volatile long&)_Uses;
if (_Count == 0)
return (false);
//把_Uses+1,循环的原因是怕失败,失败的原因是因为锁?
if (_MT_CMPX(_Uses, _Count + 1, _Count) == _Count)
return (true);
}
}
weak_ptr
将一个weak_ptr绑定到一个shared_ptr s_p ,不会增加s_p的引用计数。
weak_ptr<int> w_p(pa);
进入构造函数
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 _Other
this->_Resetw(_Other);
}
进入基类_Ptr_base里
template<class _Ty2>
void _Resetw(const _Ptr_base<_Ty2>& _Other)
{ // release weak reference to resource and take _Other._Ptr
_Resetw(_Other._Ptr, _Other._Rep);
}
template<class _Ty2>
//第一个参数就是原生指针,第二个参数就是shared_ptr的基类里的_Ref_count_base *_Rep;
//当然啦,weak_pte也有一个_Ref_count_base *_Rep和我们引用的shared_ptr共用一个
//_Ref_count_base ;_Ref_count_base 里有shared_ptr引用
//计数与weak_ptr引用计数,参照我们上面的图
void _Resetw(_Ty2 *_Other_ptr, _Ref_count_base *_Other_rep)
{ // point to _Other_ptr through _Other_rep
if (_Other_rep)
_Other_rep->_Incwref();//增加weak_ptr引用计数
if (_Rep != 0)//若此weak_ptr之前有引用某shared_ptr,将之前的weak_ptr引用计数减一
_Rep->_Decwref();
_Rep = _Other_rep;//此weak_ptr与引用的shared_ptr共用一个_Ref_count_base
_Ptr = _Other_ptr;//赋值原生指针
}
_Ref_count_base里使 _Weaks +1
void _Incwref()
{ // increment weak reference count
_MT_INCR(_Mtx, _Weaks);
}
enable_shared_from_this
存在原因
假如有如下几行代码
class A{
public:
int data;
}
int *pA = NULL;
void main(){
pA = new A;
shared_ptr<A> s_p(pA);
}
假设你现在又有想在一个地方,非main作用域里用shared_ptr引用pA,你会怎样做呢,下面这样???
shared_ptr<A> s_p2(pA);
这样是不行滴。有两个shared_ptr分别引用pA,但每个shared_ptr对pA的引用计数都是1,那么当其中一个shared_ptr销毁的时候,会把pA指向的内存释放掉,另一个shared_ptr再引用就不出现不可预知的问题了。
所以应该s_p2在引用原生指针的时候,应该像下面这样。
class A:public enable_shared_from_this<A>
{
public:
int data;
}
int *pA = NULL;
void main(){
pA = new A;
shared_ptr<A> s_p1(pA);
}
//下面是x.cpp文件中
shared_ptr<A> s_p2 = pA->shared_from_this();
不过在给s_p2赋值之前,一定要先有一个s_p1引用pA方可。