shared_ptr实现分析

1,667 阅读7分钟

shared_ptr实现

shared_ptr为c++告别裸指针,使更简单的对象管理提供了一个工具,下面就gcc4.9中shared_ptr的实现,以及与shared_ptr相关的几个概念到源码中寻找答案

这里只列出了部分源码

shared_ptr定义

shared_ptr.h

template<typename _Tp>
    class shared_ptr : public __shared_ptr<_Tp>
    {
}

shared_ptr只是一个壳子,提供了外界的调用接口,真正的实现都在__shared_ptr基类中

__shared_ptr<>

   template<typename _Tp, _Lock_policy _Lp>
    class __shared_ptr
    {
      _Tp*         _M_ptr;         // Contained pointer.
      __shared_count<_Lp>  _M_refcount;    // Reference counter.
    };

_Lock_policy为在count操作中使用锁的policy,分为无锁/mutex/atomic

__shared_count<>
  template<_Lock_policy _Lp>
    class __shared_count
    {
private:
      friend class __weak_count<_Lp>;


      _Sp_counted_base<_Lp>*  _M_pi;
    };
构造函数:
     template<typename _Ptr>
        explicit
  __shared_count(_Ptr __p) : _M_pi(0)
  {
    __try
      { // _Sp_counted_ptr继承自_Sp_counted_base<_Lp>
        // 成员只有一个:_Ptr _M_ptr,这里要继承一下,是为了直接在counted里面去释放shared_ptr的数据成员
        // 还要保留一份数据成员的指针,提供了_M_dispose方法来删除数据成员
        // 两个count成员初始化为1
        _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
      }
    __catch(...)
      {
        delete __p;
        __throw_exception_again;
      }
  }
复制构造
__shared_count(const __shared_count& __r) noexcept
      : _M_pi(__r._M_pi)  // 指针浅拷贝
      {
  if (_M_pi != 0)
    _M_pi->_M_add_ref_copy(); // _M_use_count+1
      }
赋值构造:
__shared_count&
      operator=(const __shared_count& __r) noexcept
      {
  _Sp_counted_base<_Lp>* __tmp = __r._M_pi; // 浅拷贝
  if (__tmp != _M_pi) // 不相等才赋值,相等就直接返回了
    {
      if (__tmp != 0) // _M_use_count自增1
        __tmp->_M_add_ref_copy(); 
      if (_M_pi != 0) // 成员变量释放,并赋值入参值
        _M_pi->_M_release(); 
      _M_pi = __tmp;
    }
  return *this;
      }
析构
~__shared_count() noexcept
{
  if (_M_pi != nullptr)
   // _M_use_count=1时,调用  _M_dispose()释放数据成员;_M_weak_count-1也等于0,执行_M_destroy()
    _M_pi->_M_release();
}

可以看到,__shared_count在构造函数中令_M_use_count和_M_use_count都为1,而复制和赋值构造中只给_M_use_count加1, 并没有操作_M_use_count的增减

_Sp_counted_base<_Lp>

实现两个count的安全读写/add/release等,执行计数器的原子操作,以及对shared_ptr堆上资源的释放。

enum _Lock_policy { _S_single, _S_mutex, _S_atomic };

_Lock_policy在不同的情况下,用宏定义了默认policy为三个枚举之一

  // 这里对lock policy做了默认特化
  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) { } // 构造后都为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(); // _M_use_count+1  
      void _M_add_ref_lock(); // _M_use_count为0抛异常,否则_M_use_count++
      bool _M_add_ref_lock_nothrow(); // _M_use_count为0返回false,否则_M_use_count++返回true


      void _M_release() noexcept; // _M_use_count=1时,调用  _M_dispose()释放数据成员;_M_weak_count-1也等于0,执行_M_destroy()
      void _M_weak_add_ref() noexcept; // _M_weak_count + 1
      void _M_weak_release() noexcept; // _M_weak_count-1如果等于0的话,那么执行_M_destroy()      
      long _M_get_use_count() const noexcept;// 获取_M_use_count的值
           
    private:  
      _Sp_counted_base(_Sp_counted_base const&) = delete;
      _Sp_counted_base& operator=(_Sp_counted_base const&) = delete; // 不允许复制/赋值
     // typedef int _Atomic_word;
      _Atomic_word  _M_use_count;     // #shared
      _Atomic_word  _M_weak_count;    // #weak + (#shared != 0)
    };

_M_use_count为共享计数

_M_weak_count为弱引用计数

函数实现

主要看部分构造函数实现

构造函数
template<typename _Tp1>
  explicit __shared_ptr(_Tp1* __p)
        : _M_ptr(__p), _M_refcount(__p)
  {
    // 一些合法性校验
    __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
    static_assert( !is_void<_Tp1>::value, "incomplete type" );
    static_assert( sizeof(_Tp1) > 0, "incomplete type" );
    // 这个函数调用,使用_M_refcount和__p为对象__p(继承了
    // enable_shared_from_this)赋值
    __enable_shared_from_this_helper(_M_refcount, __p, __p);
  }
复制构造
template<typename _Tp1>
shared_ptr(const shared_ptr<_Tp1>& __r, _Tp* __p) noexcept
  : __shared_ptr<_Tp>(__r, __p) { }
template<typename _Tp1>
  __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, _Tp* __p) noexcept
  : _M_ptr(__p), _M_refcount(__r._M_refcount) // never throws
  { }

复制构造,直接使用入参给数据指针赋值,调用_M_refcount的复制构造函数就可以了

移动构造
template<typename _Tp1, typename = typename
         std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
  shared_ptr(shared_ptr<_Tp1>&& __r) noexcept
  : __shared_ptr<_Tp>(std::move(__r)) { }
template<typename _Tp1, typename = typename
         std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
  __shared_ptr(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
  : _M_ptr(__r._M_ptr), _M_refcount()
  {
    _M_refcount._M_swap(__r._M_refcount);
    __r._M_ptr = 0;
  }

右值移动构造函数

  • 入参的count与this交换(为什么要交换呢?比构造代价小),入参数据指针=0
  • this的count与入参的交换,数据指针赋值为入参的数据指针
  • 数据指针浅拷贝
赋值构造
template<typename _Tp1>
  shared_ptr&
  operator=(const shared_ptr<_Tp1>& __r) noexcept
  {
    this->__shared_ptr<_Tp>::operator=(__r);
    return *this;
  }
template<typename _Tp1>
  __shared_ptr&
  operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
  {
    _M_ptr = __r._M_ptr;
    // 释放_M_refcount老值,赋新值,并_M_use_count加1
    _M_refcount = __r._M_refcount; // __shared_count::op= doesn't throw
    return *this;
  }

赋值构造函数:

  • 有返回值,返回值为*this,类型为shared_ptr&
  • 数据成员和count计数直接用=赋值,调用__shared_count的赋值构造函数
移动赋值构造
template<class _Tp1>
  shared_ptr&
  operator=(shared_ptr<_Tp1>&& __r) noexcept
  { // 可以通过this->__shared_ptr<_Tp>::调用吗?不是this->base吗?
    this->__shared_ptr<_Tp>::operator=(std::move(__r));
    return *this;
  }
 void swap(__shared_ptr<_Tp, _Lp>& __other) noexcept
{
  std::swap(_M_ptr, __other._M_ptr);
  _M_refcount._M_swap(__other._M_refcount);
 } // 交换智能指针,每个成员依次交换


 template<class _Tp1>
  __shared_ptr&
  operator=(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
  {
    __shared_ptr(std::move(__r)).swap(*this);
    return *this;
  }

移动赋值构造函数实现:

  • 调用移动构造函数使用入参构造一个临时__shared_ptr,然后交换临时变量和this
  • 返回*this
析构函数

shared_ptr/__shared_ptr的析构函数都没有显示实现,也就是会调用默认析构函数

默认析构函数会做什么事情

  • 释放_M_ptr栈上的空间(即指针本身),但是不会释放堆上的实际存储内容
  • 调用__shared_count的析构函数释放_M_refcount内存

__shared_count部分已经分析过,调用__shared_count析构函数会调用__shared_count_base的_M_release()同时释放数据空间和count,规则为:

  • _M_use_count减1,如果_M_use_count计数为0了,调用_M_dispose()(派生类的成员函数)释放数据成员
  • _M_weak_count减1, 如果_M_weark_count也为0了,那么执行_M_destroy()函数释放__shared_count对象

weak_ptr

定义

weak_ptr是一种弱引用,它对shared_ptr所管理的对象存在非拥有性引用,两种应用场景参见cppreference

定义比较简单,其定义以及所有实现的方法:

template<typename _Tp>
    class weak_ptr : public __weak_ptr<_Tp>
    {
    public:
      constexpr weak_ptr() noexcept
      : __weak_ptr<_Tp>() { }


      template<typename _Tp1, typename = typename
         std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
  weak_ptr(const weak_ptr<_Tp1>& __r) noexcept
  : __weak_ptr<_Tp>(__r) { }


      template<typename _Tp1, typename = typename
         std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
  weak_ptr(const shared_ptr<_Tp1>& __r) noexcept
  : __weak_ptr<_Tp>(__r) { }


      template<typename _Tp1>
  weak_ptr&
  operator=(const weak_ptr<_Tp1>& __r) noexcept;


      template<typename _Tp1>
  weak_ptr&
  operator=(const shared_ptr<_Tp1>& __r) noexcept;


      shared_ptr<_Tp>
      lock() const noexcept
      { return shared_ptr<_Tp>(*this, std::nothrow); }
    };

其只定义了一些构造函数,以及lock()方法用于判断所引用的shared_ptr对象是否被释放

其中在构造函数,只能构造空的weak_ptr,以及使用weak_ptr/shared_ptr进行构造,而不能使用原始指针进行构造

  template<typename _Tp, _Lock_policy _Lp>
    class __weak_ptr
{
      _Tp*     _M_ptr;         // Contained pointer.
      __weak_count<_Lp>  _M_refcount;    // Reference counter.
};
template<typename _Tp1>
  __weak_ptr&
  operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
  {
    _M_ptr = __r._M_ptr;
    _M_refcount = __r._M_refcount; // 调用__weak_count的赋值函数
    return *this;
  }

与__shared_ptr定义类似,只是计数器类型由__shared_count换成了__weak_count, __weak_count的定义如下,它的成员变量与__shared_count相同,但是在成员函数操作上,可以看到它只操作了_M_weak_count的增加/减少等,没有处理__M_use_count

  template<_Lock_policy _Lp>
    class __weak_count
    {
    public:
      constexpr __weak_count() noexcept : _M_pi(0) { }


      __weak_count(const __shared_count<_Lp>& __r) noexcept
      : _M_pi(__r._M_pi) // 存在吗?
      {
  if (_M_pi != 0)
    _M_pi->_M_weak_add_ref(); // 只加_M_weak_count
      }
      ~__weak_count() noexcept
      {
  if (_M_pi != 0)
    _M_pi->_M_weak_release(); //--_M_weak_count == 0, _M_destroy()(delete this)
      }
      
      __weak_count<_Lp>&
      operator=(const __shared_count<_Lp>& __r) noexcept
      {
  _Sp_counted_base<_Lp>* __tmp = __r._M_pi; // 浅拷贝为入参
  if (__tmp != 0)
    __tmp->_M_weak_add_ref(); // _M_weak_count+1
  if (_M_pi != 0)
    _M_pi->_M_weak_release(); // 释放旧的
  _M_pi = __tmp;
  return *this;
      }
      。。。。。。
    private:
      friend class __shared_count<_Lp>;


      _Sp_counted_base<_Lp>*  _M_pi;
    };

如上看起来,不考虑循环引用问题,weak_ptr似乎也可以作为智能指针使用,但是实际上是不可以的,原因在方法实现上:

  • 没有提供指向原始指针的构造函数,所以只能指向shared_ptr,不能直接指向原始指针对象
  • 没有定义*/->等操作符,不能直接操作对象

防止循环引用

众所周知,shared_ptr可能造成循环引用问题,那么weak_ptr是怎么解决循环引用问题的呢?看一个例子

class A {
public:
    shared_ptr<B> ab;
    // 这里可以加上构造/析构函数看下是否能够被调用
};
class B {
public:
    shared_ptr<A> ba;
    // 这里可以加上构造/析构函数看下是否能够被调用
};
shared_ptr<A> spa = make_shared<A>();  // 定义一个A对象
shared_ptr<B> spb = make_shared<B>();  // 定义一个A对象
spa.ab = spb; //shared_ptr计数成员_Sp_counted_base浅拷贝,use ref++
spb.ba = spa;

显然,由于A、B互相引用,都无法释放,造成内存泄漏(示例参考自blog)

怎样打破这种循环引用?cppreference的说法是

std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。

具体修改方法:

将class A的成员ab(或者class B的成员ba)的类型由shared_ptr修改为weak_ptr,这两种改法有什么不同呢?就是析构顺序会有差异,拥有weak_ptr的对象后被释放

再从源码实现来看原因,为什么类成员类型修改为weak_ptr之后,class A/B的析构函数就可以调用成功了呢?假设把B.ba改为weak_ptr类型

  • make_shared之后,spa/spb的_M_use_count=1, _M_weak_count=1
  • 互相赋值语句发生后
    • A.ab成员执行赋值语句:浅拷贝spb的计数指针_M_pi后,给_M_use_count加1;所以A.ab与spb的计数值:_M_use_count为2,_M_weak_count=1;
    • B.ba(weak)成员执行赋值语句:调用weak_ptr(weak_count)的赋值构造函数(入参spa, shared类型,实际是浅拷贝了spa的计数器)后,给_M_weak_count加1;B.ba与spa的计数值, _M_use_count为1,_M_weak_count=2
  • spa/spb(shared_ptr)析构时,如前分析,如果_M_use_count计数为0了就会释放数据成员,spa和spb的_M_use_count分别为1和2,调用各自的析构函数后首先spa计数减为0被释放

同理,如果没有使用weak_ptr,最终spa和spb的_M_use_count都为2,调用shared_ptr的析构函数,_Sp_counted_base两个计数值都无法减为0无法析构

make_shared

    template<typename _Tp, typename _Alloc, typename... _Args>
    inline shared_ptr<_Tp>
    allocate_shared(const _Alloc& __a, _Args&&... __args)
    {
      return shared_ptr<_Tp>(_Sp_make_shared_tag(), __a,
           std::forward<_Args>(__args)...); // 调用相关构造函数
    }
  // make_shared
  template<typename _Tp, typename... _Args>
    inline shared_ptr<_Tp>
    make_shared(_Args&&... __args)
    {
      typedef typename std::remove_const<_Tp>::type _Tp_nc;
      return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
               std::forward<_Args>(__args)...);
    }

shared_ptr为该构造函数提供了一个接口

    private:
      // This constructor is non-standard, it is used by allocate_shared.
      // struct _Sp_make_shared_tag { };为了特化
      template<typename _Alloc, typename... _Args>
  shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
       _Args&&... __args)
  : __shared_ptr<_Tp>(__tag, __a, std::forward<_Args>(__args)...)
  { }

__shared_ptr

      template<typename _Alloc, typename... _Args>
  __shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
         _Args&&... __args)
  : _M_ptr(), _M_refcount(__tag, (_Tp*)0, __a,
        std::forward<_Args>(__args)...)
  {
    // _M_ptr needs to point to the newly constructed object.
    // This relies on _Sp_counted_ptr_inplace::_M_get_deleter.
    void* __p = _M_refcount._M_get_deleter(typeid(__tag)); // 支持__GXX_RTTI时使用类型识别
    _M_ptr = static_cast<_Tp*>(__p); // _M_ptr为新构建对象
    // 设置enable_shared_from_this的_M_weak_this
    __enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);
  }

enable_shared_from_this

enable_shared_from_this主要解决的问题:

派生类可以创建一个shared_ptr,并且让他指向已经存在的shared_ptr共享所有权

使用:

  • 继承这个类,这样你就得到一个weak_ptr的成员,在构造shared_ptr时会为_M_weak_this赋值,使其指向你新构造的shared_ptr
  • 使用shared_from_this获取这个派生类的_M_weak_this对应的shared_ptr指针
  template<typename _Tp>
    class enable_shared_from_this
    {
    protected:
      constexpr enable_shared_from_this() noexcept { }


      enable_shared_from_this(const enable_shared_from_this&) noexcept { }


      enable_shared_from_this&
      operator=(const enable_shared_from_this&) noexcept
      { return *this; } // 返回他自己


      ~enable_shared_from_this() { }


    public:
      shared_ptr<_Tp>
      shared_from_this()
      { return shared_ptr<_Tp>(this->_M_weak_this); }


      shared_ptr<const _Tp>
      shared_from_this() const
      { return shared_ptr<const _Tp>(this->_M_weak_this); }


    private:
      template<typename _Tp1>
  void
  _M_weak_assign(_Tp1* __p, const __shared_count<>& __n) const noexcept
  { _M_weak_this._M_assign(__p, __n); } // 赋值weak_ptr的数据成员=__p,计数器=__n


  // __enable_shared_from_this_helper功能:
  // 使用输入参数:数据__px和shared_ptr __pe为weak_ptr赋值
      template<typename _Tp1, typename _Tp2>
  friend void
  __enable_shared_from_this_helper(const __shared_count<>&,
           const enable_shared_from_this<_Tp1>*,
           const _Tp2*) noexcept;


      mutable weak_ptr<_Tp>  _M_weak_this; // 仅有成员变量,weak_ptr
    };

使用weak_ptr构造__shared_ptr:

      template<typename _Tp1>
  explicit __shared_ptr(const __weak_ptr<_Tp1, _Lp>& __r)
  : _M_refcount(__r._M_refcount) // may throw默认复制构造浅拷贝
  {
    __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)


    // It is now safe to copy __r._M_ptr, as
    // _M_refcount(__r._M_refcount) did not throw.
    _M_ptr = __r._M_ptr;
  }

待补充……