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
但是注意, 这样构造的shared_ptr并不是与其他shared_ptr共享一个计数器, 那么, 在析构时就会导致对象被重复释放, 从而引发错误.
正确做法是继承enable_shared_from_this类, 调用sahred_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的源码)
大部分实现是通过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为什么不这样实现呢?).
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_obj的shared_ptr<T> sp1会对g_obj的weak_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 { }
其中_Yp是shared_ptr管理的对象的类型. 这两个模板函数表示:
当_Yp是make_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_refcount是shared_ptr的成员, 用来记录_Yp被多少shared_ptr管理. 这样, 就完成了对weak_ptr的处理, 使其成为一个有效值. 在以后调用shared_from_this()函数时, 就能利用weak_this调用shared_ptr的构造函数, 从而生成一个共享同一对象的shared_ptr.
上文部分代码参考了cppreference上的实例代码和gcc的源码
上一篇: Windows Developer Day Review
下一篇: 优先级队列的实现
推荐阅读
-
C#编程实现统计文件夹内文件和隐藏文件的方法示例
-
C#实现输入10个数存入到数组中并求max和min及平均数的方法示例
-
c#用Treeview实现FolderBrowerDialog 和动态获取系统图标(运用了Win32 dll类库)
-
android开发之蜂鸣提示音和震动提示的实现原理与参考代码
-
android 键盘事件和屏幕事件的运行原理及交互实现
-
自定义RadioButton和ViewPager实现TabHost带滑动的页卡效果
-
Python实现的Excel文件读写类
-
Python实现字典的key和values的交换
-
Java基础篇_有关接口和抽象类的几道练习题(分享)
-
Java实现的微信图片处理工具类【裁剪,合并,等比例缩放等】