share_ptr、weak_ptr、enable_shared_from_this理解

章高爽
2023-12-01

shared_ptr

继承体系和关键成员

由下图可知,一个shared_ptr对象,里面其实包含两个指针,一个指针指向被管理对象(manged object),一个指向管理对象(manager object),其中管理对象(manager object)中也包含被管理对象的指针,因为它要负责什么实际析构被管理对象。而shared_ptr外层也包含原始指针是为了方便*,->操作

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


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

  template<_Lock_policy _Lp>
    class __shared_count {
     friend class __weak_count<_Lp>;
      _Sp_counted_base<_Lp>*  _M_pi;
}

 template<_Lock_policy _Lp = __default_lock_policy>
    class _Sp_counted_base
    : public _Mutex_base<_Lp> {

   // 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; }

      _Atomic_word  _M_use_count;     // #shared
      _Atomic_word  _M_weak_count;    // #weak + (#shared != 0)
}

  template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
    class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp>

正确使用share_ptr的用法是,多个share_ptr 必须共享同一个管理对象,否则会发生一个原始指针被析构多次的问题(这首先要从shared_ptr的拷贝构造或者赋值构造说起,当一个shared_ptr对象sp2是由sp1拷贝构造或者赋值构造得来的时候,实际上构造完成后sp1内部的__shared_count对象包含的指向管理对象的指针与sp2内部的__shared_count对象包含的指向管理对象的指针是相等的,也就是说当多个shared_ptr对象来管理同一个对象时,它们共同使用同一个动态分配的管理对象。这可以从下面的__share_ptr的构造函数和__shared_count的构造函数清楚的看出。)

    template<typename _Tp1>
     __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r)
     : _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) // never throws
    {__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)}
     
     
    __shared_count&
    operator=(const __shared_count& __r) // nothrow
    {
        _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;
        }
    }

weak_ptr

   __weak_count相关的赋值拷贝以及析构函数均只会影响到weak_count的值,对use_count没有影响;当weak_count为0时,释放管理对象。也就是说__weak_ptr不影响被管理对象的生命周期。同时由于__weak_ptr没有像__shared_ptr那样实现*,->等常见指针相关操作符,__weak_ptr不能直接操作被管理对象;
    __weak_count自身间的赋值以及__shared_count对__weak_count的赋值时,它们都具有同样的指向管理对象的指针;也就是说当多个__weak_ptr和__shared_ptr指向同一个被管理对象时,它们共享同一个管理对象,这就保证了可以通过__weak_ptr可以判断__shared_ptr指向的被管理对象是否存在以及获取到被管理对象的指针。

  template<typename _Tp, _Lock_policy _Lp>
    class __weak_ptr {
       private:
      // Used by __enable_shared_from_this.
      void
      _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept
      {
	_M_ptr = __ptr;
	_M_refcount = __refcount;
      }

      template<typename _Tp1, _Lock_policy _Lp1> friend class __shared_ptr;
      template<typename _Tp1, _Lock_policy _Lp1> friend class __weak_ptr;
      friend class __enable_shared_from_this<_Tp, _Lp>;
      friend class enable_shared_from_this<_Tp>;

      _Tp*	 	 _M_ptr;         // Contained pointer.
      __weak_count<_Lp>  _M_refcount;    // Reference counter.
    }


  template<_Lock_policy _Lp>
    class __weak_count {

    friend class __shared_count<_Lp>;

      _Sp_counted_base<_Lp>*  _M_pi;

     ~__weak_count() noexcept
      {
	if (_M_pi != 0)
	  _M_pi->_M_weak_release();
      }
}

 template<_Lock_policy _Lp = __default_lock_policy>
    class _Sp_counted_base
    : public _Mutex_base<_Lp> {
     _Atomic_word  _M_use_count;     // #shared
      _Atomic_word  _M_weak_count;    // #weak + (#shared != 0)
}


enable_shared_from_this

C++引入了enable_shared_from_this利用weak_ptr的特性解决了这一问题。其基本思想是通过M继承模板类enable_shared_from_this,这样对象M内部将会有一个__weak_ptr指针_M_weak_this,在第一次创建指向M的shared_ptr Pt时,通过模板特化,将会初始化_M_weak_this;这样M内部也会产生一个指向自身的weak_ptr,并且该weak_ptr内部的管理对象与Pt的管理对象是相同的(这可以从weak_ptr内部的_M_assign函数看出)。

  /**
   *  @brief Base class allowing use of member function shared_from_this.
   */
  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); }

      template<typename _Tp1>
	friend void
	__enable_shared_from_this_helper(const __shared_count<>& __pn,
					 const enable_shared_from_this* __pe,
					 const _Tp1* __px) noexcept
	{
	  if (__pe != 0)
	    __pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn);
	}

      mutable weak_ptr<_Tp>  _M_weak_this;
    };

总结

enable_shared_from_this 提供安全的替用方案,以替代 std::shared_ptr(this) 这样的表达式, 但是前提是只允许在先前已被std::shared_ptr 管理的对象上调用 shared_from_this 。因为enable_shared_from_this底层依赖的是weak_ptr,而weak_ptr需要和shared_ptr共享管理对象(manager object,所以这个manager object一定要存在)(防止this 被多次析构)。

参考链接:C++11中enable_shared_from_this的用法解析_飞狼说的博客-CSDN博客_c++ enable_shared_from_this

C++ 智能指针(shared_ptr/weak_ptr)原理分析 - CNHK19 - 博客园

 类似资料: