目录
源码版本
本篇文章分析是基于 gcc-4.9.0源代码版本进行分析,官方源码下载地址为:Index of /gnu/gcc
shared_ptr
先看shared_ptr类,位于 libstdc++-v3\include\bits :
template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp>
{
public:
/**
* @brief Construct an empty %shared_ptr.
* @post use_count()==0 && get()==0
默认构造就是调用基类 __shared_ptr的构造函数
*/
constexpr shared_ptr() noexcept : __shared_ptr<_Tp>() { }
shared_ptr(const shared_ptr&) noexcept = default;
template<typename _Tp1>
explicit shared_ptr(_Tp1* __p) : __shared_ptr<_Tp>(__p) { }
template<typename _Tp1, typename _Deleter>
shared_ptr(_Tp1* __p, _Deleter __d): __shared_ptr<_Tp>(__p, __d) { }
以上为类中小部分关键源代码:
- shared_ptr类中没有类成员变量。
- 该类继承于__shared_ptr基类(注意区分),构造函数调用了__shared_ptr的构造函数,将接管的普通指针传递给基类__shared_ptr。
- 该类没有析构函数,从智能指针最终会自动释放内存的特性来看,释放工作肯定不是在该类进行。
__shared_ptr
基类 __shared_ptr,源代码文件位于 libstdc++-v3\include\bits\shared_ptr_base.h:
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr
{
public:
typedef _Tp element_type;
constexpr __shared_ptr() noexcept: _M_ptr(0), _M_refcount() { }
//构造函数
template<typename _Tp1>
explicit __shared_ptr(_Tp1* __p): _M_ptr(__p), _M_refcount(__p)
{
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
static_assert( !is_void<_Tp>::value, "incomplete type" );
static_assert( sizeof(_Tp1) > 0, "incomplete type" );
__enable_shared_from_this_helper(_M_refcount, __p, __p);
}
~__shared_ptr() = default; //析构函数
// Allow class instantiation when _Tp is [cv-qual] void.
typename std::add_lvalue_reference<_Tp>::type
operator*() const noexcept
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return *_M_ptr;
}
_Tp* operator->() const noexcept
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return _M_ptr;
}
_Tp* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.
private:
_Tp* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.
继续分析关键代码:
- 基类中有两个成员变量,_M_ptr 为智能指针接管的普通指针, _M_refcount 即引用计数器,类型为 __shared_count ,也就是说引用计数器类是单独实现的。
- 构造函数看,_M_ptr 获得了接管的普通指针的值,而_M_refcount 的构造函数也同样传入了这个普通指针。
- 重载了
*和->运算符,由上面的子类shared_ptr子类继承使用,使得智能指针最终能拥有和普通指针一样行为。- 析构函数 = default,表明什么也没做,说明接管的普通指针也不是在这里释放的,所以有可能是由_M_refcount 所属类型 __shared_count计数器类来完成释放内存这个工作。
简要描绘一下:

__shared_count
继续来看计数器的实现,__shared_count类位于 libstdc++-v3\include\bits\shared_ptr_base.h :
template<_Lock_policy _Lp>
class __shared_count
{
public:
constexpr __shared_count() noexcept : _M_pi(0) { }
// 构造函数
template<typename _Ptr>
explicit __shared_count(_Ptr __p) : _M_pi(0)
{
__try
{
_M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p); //注意是用_Sp_counted_ptr类初始化
}
__catch(...)
{
delete __p;
__throw_exception_again;
}
}
//析构函数
~__shared_count() noexcept
{
if (_M_pi != nullptr)
_M_pi->_M_release();
}
//拷贝构造
__shared_count(const __shared_count& __r) noexcept : _M_pi(__r._M_pi)
{
if (_M_pi != 0)
_M_pi->_M_add_ref_copy();
}
//赋值函数
__shared_count& operator=(const __shared_count& __r) noexcept
{
_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
if (__tmp != _M_pi)
{
if (__tmp != 0)
__tmp->_M_add_ref_copy();
if (_M_pi != 0)
_M_pi->_M_release();
_M_pi = __tmp;
}
return *this;
}
private:
friend class __weak_count<_Lp>;
_Sp_counted_base<_Lp>* _M_pi;
};
继续分析关键代码:
- 该类中有一个成员属性,
_M_pi计数器,类型为_Sp_counted_base。- 只有构造函数为
_M_pi分配了内存,该类并没有直接持有从前面一直传递过来的那个普通指针,而是继续将其传递给_M_pi,所以内存的释放也不是直接在该类进行的。- 拷贝构造函数没有分配内容,而是把拷贝对象的_M_pi直接拿过来,类似于浅拷贝,然后调用了_M_pi的_M_add_ref_copy方法,增加了一次引用计数。
- 赋值函数也是同样的道理,但由于赋值函数的特殊性(当赋值对象原先就存在时调用赋值函数,否则调用拷贝构造函数),要先调用_M_pi的_M_release方法将自己持有的内存释放掉,其余操作和拷贝构造函数是一样的。
- 从析构函数中可以看到,里面并没有直接释放掉为
_M_pi分配的内存,而是调用了_M_pi的_M_release方法,可以大概猜测是通过_M_release方法释放了_M_pi的内存(delete this指针)。- 由于
__shared_count里面的方法都是借助_M_pi实现的,并且到这里都还没有见到释放那个普通指针的代码,所以还是得继续看_M_pi究竟做了什么工作,接下来继续看_Sp_counted_base的实现。
_Sp_counted_base
_Sp_counted_base 位于 libstdc++-v3\include\bits\shared_ptr_base.h:
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp>
{
public:
_Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) { }
virtual ~_Sp_counted_base() noexcept { }
// Called when _M_use_count drops to zero, to release the resources
// managed by *this.
virtual void _M_dispose() noexcept = 0;
// Called when _M_weak_count drops to zero.
virtual void _M_destroy() noexcept { delete this; }
virtual void*
_M_get_deleter(const std::type_info&) noexcept = 0;
void _M_add_ref_copy()
{ __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }
void _M_add_ref_lock();
bool _M_add_ref_lock_nothrow();
void _M_release() noexcept
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
_M_dispose();
if (_Mutex_base<_Lp>::_S_need_barriers)
{
_GLIBCXX_READ_MEM_BARRIER;
_GLIBCXX_WRITE_MEM_BARRIER;
}
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count,-1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
_M_destroy();
}
}
}
void _M_weak_add_ref() noexcept
{ __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); }
void _M_weak_release() noexcept
{
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
if (_Mutex_base<_Lp>::_S_need_barriers)
{
// See _M_release(),
// destroy() must observe results of dispose()
_GLIBCXX_READ_MEM_BARRIER;
_GLIBCXX_WRITE_MEM_BARRIER;
}
_M_destroy();
}
}
long _M_get_use_count() const noexcept
{
// No memory barrier is used here so there is no synchronization
// with other threads.
return __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);
}
private:
_Sp_counted_base(_Sp_counted_base const&) = delete;
_Sp_counted_base& operator=(_Sp_counted_base const&) = delete;
_Atomic_word _M_use_count; // #shared
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
};
继续分析:
- 可以看到该类定义了虚析构函数。
- 该类有两个类成员:_M_use_count(引用计数) 与 _M_weak_count(弱引用计数),对这两个数的操作需要具有原子性。这里需要注意,前者其实表达的是当前有多少个强指针在引用内部指针;后者表达的是当前 shared_count类型的使用次数,即如果该计数的值为0,则需要释放掉上面 __shared_ptr基类中的成员: _Sp_counted_base<_Lp>* _M_pi;
- _M_release方法是该类的关键,可以看到先将_M_use_count自减1,然后判断自减前_M_use_count的值是否为1(无其他人引用),如果为1,则调用_M_dispose方法(虚函数,由派生类实现,估计是释放前面一直说的那个由智能指针接管的普通指针)。接下来将_M_weak_count自减1,然后判断自减前_M_weak_count的值是否为1(无其他人引用),如果为1,则调用_M_destroy方法,而_M_destroy方法里面释放了this指针,这点和前面的猜测一致。
- 从_M_release函数可以看出,智能指针所接管的指针的释放内存工作只和_M_use_count有关,当_M_use_count减完时就会将其释放了,而_M_weak_count也是有作用的,他负责释放_Sp_counted_base本身,这也就是为什么weak_ptr可以保证智能指针这个对象有效,但不保证智能指针所引用的指针有效的原因了(这点和shared_ptr、weak_ptr的定义是完全一致的)。
- _M_add_ref_copy方法将引用计数_M_use_count加一,_M_weak_add_ref方法将弱引用计数_M_weak_count加一,这个自增过程是具有原子性的。
_Sp_counted_ptr
_Sp_counted_ptr类是_Sp_counted_base的子类,位于libstdc++-v3\include\bits\shared_ptr_base.h:
template<typename _Ptr, _Lock_policy _Lp>
class _Sp_counted_ptr final : public _Sp_counted_base<_Lp>
{
public:
explicit
_Sp_counted_ptr(_Ptr __p) noexcept
: _M_ptr(__p) { }
virtual void
_M_dispose() noexcept
{ delete _M_ptr; }
virtual void
_M_destroy() noexcept
{ delete this; }
virtual void*
_M_get_deleter(const std::type_info&) noexcept
{ return nullptr; }
_Sp_counted_ptr(const _Sp_counted_ptr&) = delete;
_Sp_counted_ptr& operator=(const _Sp_counted_ptr&) = delete;
private:
_Ptr _M_ptr;
};
该类内容不多:
- 从代码中可以看到
_Sp_counted_ptr是_Sp_counted_base的派生类,并且__shared_count在初始化_M_pi时用的也是_Sp_counted_ptr。_M_dispose方法的实现里确实删除了一开始shared_ptr接管的指针,_M_destroy方法用于释放自己的内存(由__shared_count调用)。
总结
看完前面分析的内容再回过头来看,_Sp_counted_base的_M_add_ref_copy方法是整个流程的关键,它实现了引用计数器的增加,那么在何时调用它就是关键了。
通过在代码中检索,可以查到__shared_count的赋值构造函数和拷贝构造函数调用了它(其实也只有可能是这里,因为只有它的类成员有_Sp_counted_base)。
实现的整个流程:
- __shared_count成员_M_pi只会初始化一次(构造函数中分配内存初始化的)。
- 后面调用拷贝构造时(这个行为由__shared_ptr类触发,__shared_ptr的拷贝构造函数和赋值函数都会调用__shared_count 类中的拷贝构造函数),__shared_count只是简单复制了_M_pi而已,并没有重新分配内存,然后再调用_M_add_ref_copy增加一次引用计数,这样就实现了shared_ptr每多一份拷贝就增加一次引用计数的特性了。
- 每一个__shared_count被析构都会使引用计数减一,减完就将智能指针持有的资源释放。
自己根据以上内容描绘的简要整体架构图:

新版本构造流程
下面基于VS 2017编译器下,<memory>头文件下智能指针源码进行流程分析。
创建一个新的shared_ptr:
当我们使用一个原始指针直接构造智能指针时:
shared_ptr<int>sptr( new int(3) );
- 初始化 shared_base类中的_Ty *_ptr 对象;
- 创建 shared_base类中的_Ref_count 对象;
- _Ref_count_base对象构造时,会分别为两个引用计数: _Uses_count= 1 , _weaks_count= 1
这里再次强调,_Uses_count 表达的是当前有多少个强指针在引用内部指针, _weaks_count表达的是当前 _Ref_count 对象类型的使用次数,即如果_weaks_count 计数的值为0,则会释放掉上面 _Ref_count 计数器对象。
下面是引用计数增加的情况:
将另外一个shared_ptr赋值给新的shared_ptr:
_Uses_count + 1 , _weaks_count不变。
将另外一个shared_ptr赋值给新的weak_ptr:
_Uses_count 不变, _weaks_count +1 。
从weak_ptr获取一个shared ptr:
_Uses_count + 1 , _weaks_count不变。
引用计数减少的情况:
shared_ptr析构时:
_Uses_count -1 。当 _Uses_count == 0时,则释放内部对象即 _Ty *_ptr 对象,并将_weaks_count -1。
weak_ptr析构时:
_weaks_count -1, 当_weaks_count == 0时,则释放自身_Ref_count*对象。



1018

被折叠的 条评论
为什么被折叠?



