当前位置: 首页 > article >正文

《weak_ptr源码剖析》

【weak_ptr导读】weak_ptr是一种比较特殊的智能指针,不像unique_ptr、shared_ptr一样可以托管原始的指针,它更多扮演着辅助的角色,配合shared_ptr去使用,那它背后的原理是怎样的?

1、weak_ptr初探

    weak_ptr不像shared_ptr、unique_ptr一样,有独立的.h文件和.cpp文件加以实现,首先可以看weak_ptr的源码(libstdc++-v3\include\bits\shared_ptr.h)。


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
  {
	this->__weak_ptr<_Tp>::operator=(__r);
	return *this;
  }
  
  template<typename _Tp1>
  weak_ptr& operator=(const shared_ptr<_Tp1>& __r) noexcept
  {
	this->__weak_ptr<_Tp>::operator=(__r);
	return *this;
  }
  
  shared_ptr<_Tp> lock() const noexcept
  {
       return shared_ptr<_Tp>(*this, std::nothrow);
  }
}
 

    从weak_ptr的实现可以看出,它暴露给外部的接口,只有基本的构造、拷贝赋值以及大家耳熟能详的lock接口,该类内部并没有指针成员及引用计数的成员,因为我们是没法按照下面这种方式去构造一个weak_ptr对象出来的,或者直接使用*获取weak_ptr托管的内存资源。

weak_ptr<char> wp(new char[1024]);

     更多的是按照如下的方式去获得一个weak_ptr对象。

shared_ptr<char> sp(new char[1024]);weak_ptr<char> wp = sp;

      把一个shared_ptr拷贝赋值给weak_ptr,内部也是调用__weak_ptr模板类的拷贝构造函数。那么我们进一步剖析下__weak_ptr的源码。

2、__weak_ptr初探

template<typename _Tp, _Lock_policy _Lp>
class __weak_ptr
{
  public:
    typedef _Tp element_type;
    
    constexpr __weak_ptr() noexcept
      : _M_ptr(0)
      , _M_refcount()
    {}
    
    __weak_ptr(const __weak_ptr&) noexcept = default;
    
    __weak_ptr& operator=(const __weak_ptr&) noexcept = default;
    
    ~__weak_ptr() = default;
	
    template<typename _Tp1, typename = typename std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
    __weak_ptr(const __weak_ptr<_Tp1, _Lp>& __r) noexcept
	: _M_refcount(__r._M_refcount)
    { _M_ptr = __r.lock().get(); }
	
    template<typename _Tp1, typename = typename std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
    __weak_ptr(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
	: _M_ptr(__r._M_ptr)
	, _M_refcount(__r._M_refcount)
    {}
	
    template<typename _Tp1>
    __weak_ptr& operator=(const __weak_ptr<_Tp1, _Lp>& __r) noexcept
    {
	_M_ptr = __r.lock().get();
	_M_refcount = __r._M_refcount;
	return *this;
    }
    
    template<typename _Tp1>
    __weak_ptr& operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
    {
	_M_ptr = __r._M_ptr;
	_M_refcount = __r._M_refcount;
	return *this;
    }
    
    __shared_ptr<_Tp, _Lp> lock() const noexcept
    { 
        return __shared_ptr<element_type, _Lp>(*this, std::nothrow);
    }
    
    long use_count() const noexcept
    { 
        return _M_refcount._M_get_use_count(); 
    }
	
    bool expired() const noexcept
    { 
        return _M_refcount._M_get_use_count() == 0; 
    }
    
    template<typename _Tp1>
    bool owner_before(const __shared_ptr<_Tp1, _Lp>& __rhs) const
    { 
        return _M_refcount._M_less(__rhs._M_refcount); 
    }
    
    template<typename _Tp1>
    bool owner_before(const __weak_ptr<_Tp1, _Lp>& __rhs) const
    { 
        return _M_refcount._M_less(__rhs._M_refcount); 
    }
    
    void reset() noexcept
    { 
        __weak_ptr().swap(*this); 
    }

    void swap(__weak_ptr& __s) noexcept
    {
	std::swap(_M_ptr, __s._M_ptr);
	_M_refcount._M_swap(__s._M_refcount);
    }
    
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>;
    
    /* 虽然weak_ptr不托管内存,但内部还需要维护一份内的副本_M_ptr
       从shared_ptr拷贝构造weak_ptr时,_M_ptr便指向shared_ptr托管的内存地址,
       仅仅是浅拷贝而已。调用lock接口将weak_ptr转换成shared_ptr,
       也只是将_weak_ptr管理的_M_ptr转换成shared_ptr维护的_M_ptr而已,
       同样不涉及深拷贝。                                                               
    */
    _Tp* _M_ptr;   
    __weak_count<_Lp> _M_refcount;  // Reference counter.
}
 

  __weak_ptr有两个成员变量,托管的普通内存指针_M_ptr、弱引用计数_M_refcount(类型为__weak_count),__weak_ptr的弱引用计数_M_refcount一般是从__shared_ptr或者其它的__weak_ptr的_M_refcount构造而来,正因为如此,用shared_ptr初始化一个weak_ptr并不会再增加shared_ptr托管内存的引用计数。既然拷贝构造不会增加托管内存的引用计数,那weak_ptr的lock方法会增加托管内存的引用计数吗?答案是会的哈!且看如下示例:   
    

图片

   调用lock接口,归根结底就是将weak_ptr构造出一个shared_ptr对象出来,回顾上一节《shared_ptr源码剖析》,shared_ptr的构造函数最终会调用到__shared_ptr的构造函数。 

shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t)    : __shared_ptr<_Tp>(__r, std::nothrow){
  
  }

__shared_ptr构造函数的源码如下:   

__shared_ptr(const __weak_ptr<_Tp, _Lp>& __r, std::nothrow_t)    : _M_refcount(__r._M_refcount, std::nothrow){
  
      _M_ptr = _M_refcount._M_get_use_count() ?               __r._M_ptr : nullptr;}

    weak_ptr的lock接口,最终会用_weak_count去构造一个__shared_count,如果引用计数不为0就正常构造一个shared_ptr出来。那当前shared_ptr的引用计数为何会增加1呢?且看__shared_count的构造函数。


template<_Lock_policy _Lp>
inline __shared_count<_Lp>::__shared_count(const __weak_count<_Lp>& __r, std::nothrow_t)
    :_M_pi(__r._M_pi)
{
    if (_M_pi != nullptr)
        if (!_M_pi->_M_add_ref_lock_nothrow())
            _M_pi = nullptr;
}

template<>
inline bool _Sp_counted_base<_S_single>::_M_add_ref_lock_nothrow()
{
    if (_M_use_count == 0)
        return false;
    ++_M_use_count;
    return true;
}

   __shared_count的构造函数,最终会调用它的成员变量_M_pi的 _M_add_ref_lock_nothrow接口,进行引用计数的递增操作,也即同一块内存是共用一个_Sp_counted_base的。调用weak_ptr的lock接口,最终还是会增加初始化weak_ptr的shared_ptr对象托管内存的引用计数。即解释了为啥lock接口之后,shared_ptr的引用计数会增加1。那么weak_ptr的引用计数为何也递增1呢?切看weak_ptr的use_count()接口。

long use_count() const noexcept    return _M_refcount._M_get_use_count(); }

   _M_refcount是__weak_count类型的成员变量,那么进一步剖析__weak_count的源码。  
   

3、__weak_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();
   }
   
   ......
   
   ~__weak_count() noexcept
   {
      if (_M_pi != 0)
        _M_pi->_M_weak_release();
   }
  
    long _M_get_use_count() const noexcept
    { 
      return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; 
    }
    
 private:
      friend class __shared_count<_Lp>;
      _Sp_counted_base<_Lp>*  _M_pi;
 };

   从__weak_count的源码可以看出,获取weak_ptr引用计数的接口,最终还是会调用_Sp_counted_base维护的引用计数,该引用计数和shared_ptr是同一份。那么现在大家应该知道为啥weak_ptr lock()之后,weak_ptr的引用计数也会递增1了。 

  

4、总结

    1、weak_ptr更多是对shared_ptr托管内存的一个弱引用,不参与托管内存的生命周期的管理,从__weak_ptr的析构函数可以看出,它并没有对它维护的_M_ptr 执行释放操作。

    2、weak_ptr的应用场景,在跨模块传递内存时,A模块申请的内存想传递给B模块使用时,B模块可以使用weak_ptr去接管这份内存,如果A模块没有释放这份内存,那么B模块就拿来使用;反之就不用,B模块不参与A模块的内存管理,同时也极大降低了野指针的风险。

      

   

    

    

                                                                             


http://www.kler.cn/a/510502.html

相关文章:

  • 最新版Edge浏览器加载ActiveX控件技术——allWebPlugin中间件之awp_CreateActiveXObject接口用法
  • 计算机网络 | IP地址、子网掩码、网络地址、主机地址计算方式详解
  • 数据库高可用方案-01-数据库备份还原方案
  • owasp SQL 注入-03 (原理)
  • 设计和优化用于 AR、HUD 和高级显示系统的表面浮雕光栅
  • MySQL HASH索引详解
  • 在K8S中,业务Pod数据如何存储?
  • JavaScript系列(32)-- WebAssembly集成详解
  • 数据库高可用方案-08-多版本管理
  • owasp SQL 注入-03 (原理)
  • Python爬虫-爱奇艺电视剧数据
  • Redis的部署和操作
  • 基于poll函数实现并发处理
  • 联合体(Union)
  • 根据现代业务需求设计数据架构(三)- 数据网格(Data Mesh)
  • 数据结构 数组
  • 团体程序设计天梯赛-练习集——L1-012 计算指数
  • Netty中的NioEventloop(1)
  • vue基础代码第一篇
  • 分类问题(二元,多元逻辑回归,费歇尔判别分析)spss实操
  • [手机Linux] ubuntu 错误解决
  • lanqiaoOJ 2128:重新排序 ← 一维差分
  • 【优先算法】滑动窗口--结合例题详解学习
  • Node.js --- 模板引擎EJS
  • 使用rpc绕过咸鱼sign校验
  • 如何让a和b的地址互换?