zoukankan      html  css  js  c++  java
  • enable_shared_from_this类的作用和实现

    使用举例

    有时候我们需要在一个被 shared_ptr 管理的对象的内部获取自己的 shared_ptr, 比如下面这个简单的例子:

    • 通过 this 指针来构造一个 shared_ptr:
     1 struct Bad
     2 {
     3     void fun()
     4     {
     5         shared_ptr<Bad> sp{this};
     6         cout<<sp->count()<<endl;
     7     }
     8 };
     9 shared_ptr<Bad> sp{make_shared<Bad>()};
    10 sp->fun(); //输出为1

    但是注意, 在 func 函数构造智能指针时, 我们无法确定这个对象是不是被 shared_ptr 管理着, 因此这样构造的 shared_ptr 并不是与其他 shared_ptr 共享一个计数器, 那么, 在析构时就会导致对象被重复释放, 从而引发错误.

    现在明确一下我们的需求: 在一个对象内部构造该对象的 shared_ptr 时, 即使该对象已经被 shared_ptr 管理着, 也不会造成对象被两个独立的智能指针管理.

    这就要求我们在对象内构造对象的智能指针时, 必须能识别有对象是否已经由其他智能指针管理, 智能指针的数量, 并且我们创建智能指针后也能让之前的智能指针感知到.

    正确做法是继承 enable_shared_from_this 类, 调用 shared_from_this() 函数生成 shared_ptr, 使用如下:

     1 struct Good : public std::enable_shared_from_this<Good>
     2 {
     3   void fun()
     4   {
     5     shared_ptr<Good> sp{shared_from_this()};
     6     cout<<sp->count()<<endl;
     7   }
     8 };
     9 shared_ptr<Good> sp{make_shared<Good>()};//--------------*1*
    10 sp->fun(); //输出为2

    在类内部通过 enable_shared_from_this 定义的 shared_from_this() 函数构造一个 shared_ptr<Good> 对象, 能和其他 shared_ptr 共享 Good 对象.

    enable_shared_from_this的实现分析(基于gcc-7.2.0的源码)

    gcc是通过 weak_ptr 来实现的. 先用要管理对象(obj)的指针和已有的管理obj的 shared_ptr(sp1,...,spn) 的个数(spi->use_count())来初始化一个 weak_ptr<Obj>(&obj , spi->use_count()), 然后用这个 weak_ptr 构造一个 shared_ptr.

     1 // enable_shared_from_this的实现
     2 // 基于(/usr/include/c++/7.3.0/bits/shared_ptr.h)
     3 // 此代码是对gcc实现的简化版本, 仅作为描述原理用.    
     4 template<typename T>
     5 class enable_shared_from_this
     6 {
     7 public:
     8     shared_ptr<T> shared_from_this()
     9     {
    10         return shared_ptr<T>(this->weak_this);
    11     }
    12     shared_ptr<const T> shared_from_this() const
    13     {
    14         return shared_ptr<const T>(this->weak_this);
    15     }
    16 private:
    17     template<typename>
    18     friend class shared_ptr;
    19 
    20     template<typename T1>
    21     void _M_weak_assign(T1* p, const shared_count<>& n)
    22     {
    23       weak_this._M_assign(p, n);
    24     }
    25 
    26     mutable weak_ptr<T> weak_this;
    27 };

    enable_shared_from_this<T> 类中定义了一个 weak_ptr<T>, 起到了上文提到的从obj指针生成 shared_ptr<T> 对象的作用. 按照先前的原理, 我们可能认为是在obj初始化的时候, 同时对 weak_this 进行初始化, 但是在这段代码里显然没有对 weak_this 进行任何初始化工作(原始代码里也没有, gcc为什么不这样实现呢? 这是因为当对象没有由智能指针管理时, 这些操作是没有必要的. 所以应该把这个任务交给 shared_ptr).

    gcc在 shared_ptr<T> 的构造函数中对 weak_ptr<T> 进行处理. 从 Good 类来看, 就是在 *1* 处对 Good 对象中的 weak_ptr<Good> weak_this 进行处理, 使其指向一个有效的 Good 对象, 并修改 use_count. 上面 Good 类对 enable_shared_from_this 的使用是少数几种有效的方法, 必须保证, 如果对一个对象调用 shared_from_this(), 该对象必须是由 shared_ptr<T> 持有的. 从上一段的原理中可以理解这样做的原因: 第一个持有 Good 对象 g_objshared_ptr<T> sp1 会对 g_objweak_this 进行处理, 使其有效. 如果没有这一步, 在调用 shared_from_this() 时, weak_this 是一个无效值, 即 weak_this.expire() == true, 就会抛出异常.

    那么在 shared_ptr 的构造函数中是如何处理 weak_ptr 的呢?

    shared_ptr 中定义了这样一个函数(来自/usr/include/c++/7.3.0/bits/shared_ptr_base.h中类__shared_ptr):

     1 template<typename _Yp, typename _Yp2 = typename remove_cv<_Yp>::type>
     2 typename enable_if<__has_esft_base<_Yp2>::value>::type
     3 _M_enable_shared_from_this_with(_Yp* __p) noexcept
     4 {
     5     if(auto __base = __enable_shared_from_this_base(_M_refcount, __p))
     6         __base->_M_weak_assign(const_cast<_Yp2*>(__p), _M_refcount);
     7 }
     8 
     9 template<typename _Yp, typename _Yp2 = typename remove_cv<_Yp>::type>
    10 typename enable_if<!__has_esft_base<_Yp2>::value>::type
    11 _M_enable_shared_from_this_with(_Yp*) noexcept { }

    其中 _Ypshared_ptr 管理的对象的类型. 这两个模板函数表示:

    _Yp enable_shared_from_this 的子类时, 就会生成第一个函数, 其功能是通过 _Yp 对象的指针来调用其 _M_weak_assign 函数以修改 _Yp 对象的 weak_this 成员, 而实际上 _M_weak_assign 调用的是 _M_assign 函数. 

    否则生成第二个函数体为空的函数.

     1 // from shared_ptr_base.h class __weak_ptr, derived by weak_ptr
     2 
     3 void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept
     4 {
     5     if (use_count() == 0)
     6     {
     7         _M_ptr = __ptr;
     8         _M_refcount = __refcount;
     9     }
    10 }

    _M_enable_shared_from_this_with 函数在 shared_ptr<_Yp> 的构造函数中被调用, 从而检测 _Yp 是否继承自 make_shared_from_this, 并进行相应的处理. 这里的 _M_refcountshared_ptr 的成员, 用来记录 _Yp 被多少 shared_ptr 管理. 这样, 就完成了对 weak_ptr 的处理, 使其成为一个有效值. 在以后调用 shared_from_this() 函数时, 就能利用 weak_this 调用 shared_ptr 的构造函数, 从而生成一个共享同一对象的 shared_ptr.

    上文部分代码参考了cppreference上的实例代码和gcc的源码

  • 相关阅读:
    PAT1066(AVL树)
    判断是对象,还是数组对象
    electron打包成.exe后限制只启动一个应用
    electron启动出现短暂的白屏
    js sort方法根据数组中对象的某一个属性值进行排序
    VUE DIV模拟input框的基本处理
    vue 设置 input 为不可以编辑
    VUE实现限制输入框最多输入15个中文,或者30个英文
    electron 点击事件无效
    electron监听系统托盘,electron是否最小化到系统托盘
  • 原文地址:https://www.cnblogs.com/yang-wen/p/8573269.html
Copyright © 2011-2022 走看看