赞
踩
目录
本篇文章分析是基于 gcc-4.9.0源代码版本进行分析,官方源码下载地址为:Index of /gnu/gcc
先看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,源代码文件位于 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类位于 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 位于 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_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)。
实现的整个流程:
自己根据以上内容描绘的简要整体架构图:
下面基于VS 2017编译器下,<memory>头文件下智能指针源码进行流程分析。
创建一个新的shared_ptr:
当我们使用一个原始指针直接构造智能指针时:
shared_ptr<int>sptr( new int(3) );
这里再次强调,_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*对象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。