vs2010的std::tr1::shared_ptr和boost的shared_ptr比起来简单易懂,代码短小精悍。我没有细读过boost::shared_ptr的代码,粗略看过,细节上的区别还是很大的。
shared_ptr是一个模板类,派生自_Ptr_base。其中_Ptr_base内记录的_Ty*是用户创建shared_ptr时传入的要管理的指针;而_Ptr_base内还有一个成员变量,是一个_Ref_count_base的指针。它是实现引用计数功能的主力。
_Ref_count_base 是在shared_ptr创建的时候new出来的。
_Ref_count_base有3个子类,分别对应shared_ptr构造时的3种形式。在shared_ptr的实现中,会根据调用的shared_ptr构造函数的不同,使用不同类型的_Ref_count_base创建。
_Ref_count_base中保存了两个引用计数:
l _Uses:用于shared_ptr,表示该_Ref_count_base被几个shared_ptr引用了。当_Uses等于0时,会释放_Ref_count_base中保存的对象(也就是用户传入的指针)。
l _Weaks:用于weak_ptr,表示该_Ref_count_base被几个weak_ptr引用了。当_ Weaks等于0时,会释放_Ref_count_base自身。
有了这两个引用计数,就能实现只能指针管理资源基本功能以及野指针检查的功能。
在shared_ptr和weak_ptr的构造函数中均有几个显眼的重载项,例如:
template
shared_ptr(const shared_ptr<_Ty2>& _Other,
typename enable_if
void *>::type * = 0)
{ // construct shared_ptr object that owns same resource as _Other
this->_Reset(_Other);
}
这里enable_if
这其实是一个模版元编程的一个惯用手法,即type_traits。利用编译期推导的特性,让编译器选择何时重载的函数。
这里的意思就是如果出现这样的代码:
class A{};
class B:public A{};
shared_ptr pA(new A);
shared_ptr pB(pA);
当执行shared_ptr pB(pA);时会走上面的函数。换句话说,如果B和A没有派生关系,那么编译器就找不到对应的重载函数,就会在编译期提示错误。
OK,深入到细节仔细看一下这个type_traits的实现。其中,is_convertible<_Ty2 *, _Ty *>会被展开为以下代码:
template
struct _Cat_base;
template<>
struct _Cat_base
: false_type
{ // base class for type predicates
};
template<>
struct _Cat_base
: true_type
{ // base class for type predicates
};
template
struct is_convertible :
_Cat_base
is_void<_To>::value ||
__is_convertible_to(_From, _To)>
{ // determine whether _From is convertible to _To
};
其中__is_convertible_to是一个编译器内部支持的函数,能够获取到两个类之间是否能够转换。详见http://technet.microsoft.com/zh-cn/library/ms177194(v=vs.80)。当然这里还有两种特殊情况,就是如果_From和_To如果都是void类型那么也是可以转换的。
_Cat_base在这里只起到一个把多个布尔值组合起来的功能。_Cat_base有1个泛型实现有2个特化版本。
再看enable_if的实现,手法和_Cat_base如出一辙。实现偏特化的类让编译器选择。
// TEMPLATE CLASS enable_if
template class _Type = void> struct enable_if { // type is undefined for assumed !_Test }; template struct enable_if { // type is _Type for _Test typedef _Type type; }; 也就是说只有当is_convertible求值为_Cat_base 选取一个带有删除器的构造函数作为例子: template class _Dx> shared_ptr(_Ux *_Px, _Dx _Dt) { // construct with _Px, deleter _Resetp(_Px, _Dt); } 在该构造函数中,转发调用下面的函数: template class _Dx> void _Resetp(_Ux *_Px, _Dx _Dt) { // release, take ownership of _Px, deleter _Dt _TRY_BEGIN // allocate control block and reset _Resetp0(_Px, new _Ref_count_del<_Ux, _Dx>(_Px, _Dt)); _CATCH_ALL // allocation failed, delete resource _Dt(_Px); _RERAISE; _CATCH_END } 可以看出,在该函数中new了一个_Ref_count_del的类。_Resetp0这个函数是基类_Ptr_base的一个成员函数: template void _Resetp0(_Ux *_Px, _Ref_count_base *_Rx) { // release resource and take ownership of _Px this->_Reset0(_Px, _Rx); _Enable_shared(_Px, _Rx);(空实现) } 在这个函数中调用了_Ref_count_base的_Reset0函数,同时还有一个_Enable_shared函数,不过_Enable_shared函数只有当_Px派生自enable_shared_from_this的类有用,对于一般类而言是空实现。 接着看一下构造过程的终点:_Ref_count_base:: Reset0函数: void _Reset0(_Ty *_Other_ptr, _Ref_count_base *_Other_rep) { // release resource and take new resource if (_Rep != 0) _Rep->_Decref(); _Rep = _Other_rep; _Ptr = _Other_ptr; } 很简单的赋值操作,引用计数实现的惯用手法。如果引用计数已经有值的话需要先减去引用计数。下面看减去引用计数的操作: void _Decref() { // decrement use count if (_MT_DECR(_Mtx, _Uses) == 0) { // destroy managed resource, decrement weak reference count _Destroy(); //纯虚函数 _Decwref(); } } 这里如果引用计数减到0那么就调用_Destroy函数和_Decwref函数。这里_Destroy函数是_Ref_count_base的一个纯虚函数。该纯虚函数的意图是:如何删除引用计数管理的对象。很容易想象,有删除器的实现自然是调用删除器来完成这个动作,没有删除器的话就调用delete。 再看_Decwref函数。这个函数和weak_ptr有关系,后面讲。实现可以先看下: void _Decwref() { // decrement weak reference count if (_MT_DECR(_Mtx, _Weaks) == 0) _Delete_this(); } _Delete_this函数也是_Ref_count_base的一个纯虚函数。该纯虚函数的意图是:释放自身的方式(即_Ref_count_base)。 ~shared_ptr() { // release resource this->_Decref(); } 调用_Ptr_base:: _Decref()函数: void _Decref() { // decrement reference count if (_Rep != 0) _Rep->_Decref(); } 调用_Ref_count_base:: _Decref()函数: void _Decref() { // decrement use count if (_MT_DECR(_Mtx, _Uses) == 0) { // destroy managed resource, decrement weak reference count _Destroy(); //纯虚函数 _Decwref(); } } 接下来的过程就和前文中提到的shared_ptr构造时的过程一模一样了。 从shared_ptr的析构过程可以看出来。由于weak_ptr引用计数的原因,有可能当shared_ptr析构了以后,对应的管理的对象也被释放了,但是_Ref_count_base却还在内存中留存。一定要等到所有的weak_ptr都释放以后才会彻底释放。 拷贝构造的过程其实和构造函数流程基本一致: shared_ptr(const _Myt& _Other) { // construct shared_ptr object that owns same resource as _Other this->_Reset(_Other); } 到这里就会发现拷贝构造的过程和构造过程已经一样了: template void _Reset(const _Ptr_base<_Ty2>& _Other) { // release resource and take ownership of _Other._Ptr _Reset(_Other._Ptr, _Other._Rep, false); } shared_ptr(_Myt&& _Right) : _Mybase(_STD forward<_Myt>(_Right)) { // construct shared_ptr object that takes resource from _Right } 这里直接调用的是基类的rvalue拷贝构造: template _Ptr_base(_Ptr_base<_Ty2>&& _Right) : _Ptr(_Right._Ptr), _Rep(_Right._Rep) { // construct _Ptr_base object that takes resource from _Right _Right._Ptr = 0; _Right._Rep = 0; } 由于_Ptr_base里面也没什么内存块,所以这里的右值拷贝基本和普通拷贝没什么区别。 赋值的过程,调用拷贝构造然后swap: template _Myt& operator=(const shared_ptr<_Ty2>& _Right) { // assign shared ownership of resource owned by _Right shared_ptr(_Right).swap(*this); return (*this); } 首先是把参数临时拷贝了一份,然后用 STL中随处可见的swap大法。保证异常安全。 void swap(_Myt& _Other) { // swap pointers this->_Swap(_Other); } 这里调用的是基类_Ptr_base的函数: void _Swap(_Ptr_base& _Right) { // swap pointers _STD swap(_Rep, _Right._Rep); _STD swap(_Ptr, _Right._Ptr); } _STD是一个宏,展开就是std::。这里调用的是std::swap函数,该函数在utility.h: // TEMPLATE FUNCTION swap (from template void swap(_Ty& _Left, _Ty& _Right) { // exchange values stored at _Left and _Right _Ty _Tmp = _Move(_Left); _Left = _Move(_Right); _Right = _Move(_Tmp); } 再看_Move函数: // TEMPLATE FUNCTION _Move template typename tr1::_Remove_reference<_Ty>::_Type&& _Move(_Ty&& _Arg) { // forward _Arg as movable return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg); } 到这里也就仅仅是把右值引用拿过来,通过分析得知,基类_Ptr_base的swap中模板推导参数就应该是_Ref_count_base。所以swap函数中实际会调用_Ref_count_base的右值=,不过_Ref_count_base并没有定义该操作符,所以最终也就是普通的指针赋值了。 weak_ptr同样是派生自_Ptr_base,weak_ptr本身没有任何成员变量。但是在_Ref_count_base中保存了一个专门用于weak_ptr的引用计数。 构造过程和shared_ptr也差不多: template weak_ptr(const shared_ptr<_Ty2>& _Other, typename enable_if void *>::type * = 0) { // construct weak_ptr object for resource owned by _Other this->_Resetw(_Other); } 这里稍微一个不同的地方是,这里用了一个type_traits:enable_if。但是实际上这个参数并没有作用,也没有重载的作用。 接下来就是调用基类的_Resetw函数(这里的w值得就是weak_ptr的意思): template void _Resetw(const _Ptr_base<_Ty2>& _Other) { // release weak reference to resource and take _Other._Ptr _Resetw(_Other._Ptr, _Other._Rep); } template void _Resetw(_Ty2 *_Other_ptr, _Ref_count_base *_Other_rep) { // point to _Other_ptr through _Other_rep if (_Other_rep) _Other_rep->_Incwref(); if (_Rep != 0) _Rep->_Decwref(); _Rep = _Other_rep; _Ptr = _Other_ptr; } 这里并没有判断传入的_Other_ptr是不是等于自身的_Ptr。而是字节先增加引用计数然后再把原本的类成员变量记录的weak_ptr的引用计数减去。 可以很明显的看到。每个shared_ptr构造的时候都会new一个_Ref_count_base出来,但是weak_ptr就不会了。weak_ptr只能通过shared_ptr构造,直接把shared_ptr的_Ref_count_base拿出来用了。 ~weak_ptr() { // release resource this->_Decwref(); } void _Decwref() { // decrement weak reference count if (_Rep != 0) _Rep->_Decwref(); } void _Decwref() { // decrement weak reference count if (_MT_DECR(_Mtx, _Weaks) == 0) _Delete_this(); } weak_ptr析构的时候,会将自身引用的_Ref_count_base的weak引用计数-1。在_Ref_count_base的实现中,如果weak引用计数等于0,那么就删除自身(_Ref_count_base)。 这个问题实际上很简单:weak_ptr使用expired函数判断shared_ptr是否已经失效了: bool expired() const { // return true if resource no longer exists return (this->_Expired()); } 调用的是基类的_Expired: bool _Expired() const { // test if expired return (!_Rep || _Rep->_Expired()); } 调用_Ref_count_base的_Expired(): bool _Expired() const { // return true if _Uses == 0 return (_Uses == 0); } 可以看到,实际上检查shared_ptr是不是已经被释放掉了是通过检查_Ref_count_base来实现的。也就是说_Ref_count_base中的两个引用计数都等于0时,_Ref_count_base自己才会被释放掉。 首先要说明一个 enable_shared_from_this的坑: 1、 一个类C派生自enable_shared_from_this,那么不能在类C的构造函数中调用shared_from_this()。 2、 一个类C派生自enable_shared_from_this,如果需要使用shared_from_this这个功能,那么创建C时只能使用shared_ptr,使用普通的new或者在栈上创建都是不行的。 下面看下enable_shared_from_this代码,其实不过是一个weak_ptr: template { // provide member functions that create shared_ptr to this public: typedef _Ty _EStype; shared_ptr<_Ty> shared_from_this() { // return shared_ptr return (shared_ptr<_Ty>(_Wptr)); } private: mutable weak_ptr<_Ty> _Wptr; }; 当你写下shared_ptr template void _Resetp0(_Ux *_Px, _Ref_count_base *_Rx) { // release resource and take ownership of _Px this->_Reset0(_Px, _Rx); _Enable_shared(_Px, _Rx); } 这里_Enable_shared前面说是空实现。但是针对派生自enable_shared_from_this的类就不是空实现了: template inline void _Enable_shared(_Ty *_Ptr, _Ref_count_base *_Refptr, typename _Ty::_EStype * = 0) { // reset internal weak pointer if (_Ptr) _Do_enable(_Ptr,(enable_shared_from_this } template 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); } 可以看到,在shared_ptr构造的时候,针对派生自enable_shared_from_this的类,会把类中的_Wptr这个成员变量赋值。这个操作至关重要,是enable_shared_from_this能够发挥作用的关键。 那为什么不能在构造函数中使用shared_from_this呢?这个原因很简单,因为_Wptr这个成员变量赋值是在shared_ptr的构造函数中,而shared_ptr的构造函数是在类CC构造函数之后执行的。 这里有一篇好文,是介绍boost::shared_ptr的线程安全性的:《为什么多线程读写 shared_ptr 要加锁?》http://blog.csdn.net/solstice/article/details/8547547 从数据结构上来看,shared_ptr以及weak_ptr的数据结构设计的非常简洁。只有_Ref_count_base这个成员是new出来的,它是可能被多个shared_ptr/weak_ptr实例共享的。 那么如果有两个处于不同线程的shared_ptr同时读写某一个_Ref_count_base时,如何保证线程安全呢?shared_ptr使用stl中普遍使用swap方法来将这个问题尽量减到最低。所以在shared_ptr的实现中,你会发现很多函数都是这样实现的: template _Myt& operator=(const shared_ptr<_Ty2>& _Right) { // assign shared ownership of resource owned by _Right shared_ptr(_Right).swap(*this); return (*this); } 再看_Ref_count_base,里面有两个引用计数的成员。vs2010的实现中,使用了interlock的函数对这两个引用计数成员进行操作。 #define _MT_INCR(mtx, x) _InterlockedIncrement(&x) #define _MT_DECR(mtx, x) _InterlockedDecrement(&x) #define _MT_CMPX(x, y, z) _InterlockedCompareExchange(&x, y, z) 例如: 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(); } 这样的做法使得_Ref_count_base本身是线程安全的,但是并不能完全保证能够做到多线程同时读写同一个shared_ptr对象。所以我认为这里使用interlock的函数没什么必要。 首先回顾下C++异常安全性的三个级别: l 异常安全的基本保证:如果有异常抛出,资源不会泄漏。对象保持可摧毁、可使用但是不一定可预测的状态。它使用的场合是:失败的操作已经对对象状态做了改变,但是调用者代码依然能够应付。 l 异常安全的强烈保证:如果有异常抛出,程序状态保持不变。这一级别总是蕴含这”提交或者回退”的语义。例如:如果操作失败指向容器内部的引用或者迭代器不会失效 l 不抛出异常保证:在任何情况下,函数都不会产生异常。这一点对于编写底层库函数来说尤其重要。 在std::tr1::shared_ptr的实现中,绝大多数函数中都恪守不抛出异常的原则。只有3个函数直接抛出异常的地方,不过也要看是否定义了HAS_EXCEPTIONS宏。这3个函数其实是同一个函数的3种重载,对应3种shared_ptr的构造方式: template 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 resource delete _Px; _RERAISE; _CATCH_END } template class _Dx> void _Resetp(_Ux *_Px, _Dx _Dt) { // release, take ownership of _Px, deleter _Dt _TRY_BEGIN // allocate control block and reset _Resetp0(_Px, new _Ref_count_del<_Ux, _Dx>(_Px, _Dt)); _CATCH_ALL // allocation failed, delete resource _Dt(_Px); _RERAISE; _CATCH_END } //#if _HAS_CPP0X template class _Dx, class _Alloc> void _Resetp(_Ux *_Px, _Dx _Dt, _Alloc _Ax) { // release, take ownership of _Px, deleter _Dt, allocator _Ax typedef _Ref_count_del_alloc<_Ux, _Dx, _Alloc> _Refd; typename _Alloc::template rebind<_Refd>::other _Al = _Ax; _TRY_BEGIN // allocate control block and reset _Refd *_Ptr = _Al.allocate(1); new (_Ptr) _Refd(_Px, _Dt, _Al); _Resetp0(_Px, _Ptr); _CATCH_ALL // allocation failed, delete resource _Dt(_Px); _RERAISE; _CATCH_END } 意图其实也很简单:如果调用_ Resetp0的过程中有异常抛出,把new出来的对象删除掉,然后把异常传递出去。根据_Resetp0函数代码分析,_Resetp0执行过程中最有可能抛出异常的情况有: l 内存不足,bad alloc异常被抛出 l 在调用_Ref_count_base::_Decref的过程中会调用_Destroy(),这是个纯虚函数,可能被实现为抛出异常。 l 用户实现的析构函数可能会抛出异常。 但是,仔细看这几个try包住的地方,全部都是new一个全新的_Ref_count_base,也就是说可能抛出异常的情况2和情况3不可能出现。只可能出现内存不足抛出异常的情况。所以我认为这里的异常处理画蛇添足。 ————————以上摘自:std::tr1::shared_ptr源码赏析 shared_ptr创建过程
shared_ptr的析构过程
shared_ptr拷贝构造过程
shared_ptr右值拷贝构造过程
shared_ptr赋值的过程
weak_ptr大致结构
weak_ptr构造的过程
weak_ptr的析构过程
weak_ptr是怎么实现野指针检测的
enable_shared_from_this是如何工作的
线程安全性
异常安全性