这节课讲讲
智能指针的计数器如何去管理的
以及我们自己来编写一个自己的有强弱指针关系的智能指针版本
具体而言:
按道理, 我们要跟踪源码, 为什么呢
因为我们要知道几个问题
何时我们的强指针次数会+1, 何时我们的弱指针次数会+1
我们需要去跟踪
这里我们提供跟踪的思路
强指针构造, 析构, =赋值, 拷贝构造等情况下, 计数器的变化
弱指针构造, 析构, =赋值, 拷贝构造等情况下, 计数器的变化
弱指针提升为强指针, 计数器变化
代码运行分析:
强指针构造的时候, 计数器变化情况
单步走, f5,
std::shared_ptr<int> sptr(new int(3));
进入shared_ptr
的构造函数
用的是以下类型的构造
但是这样来有一个问题, 构造的时候需要先到父类Ptr_base
, 再到子类, 让我们看下是不是这样的
单步走, 进到父类Ptr_base
到父类后, 先进行初始化操作, 将父类的内部指针_Ptr
置为空和引用次数_Rep
置为空
从父类Ptr_base
出来之后, 到真正的构造函数里
到Restp(_Px)
里, 按道理, 应该赋予内部指针_Ux _Px
, 然后引用次数变一变
单步走,f11进来(视频发现f11进不去, 直接手动进去看)
有点复杂, 有个delete _Px
删除原始的, try catch
异常处理结构
核心代码只要看_Resetp0(_Px, new _Ref_count<_UX>(_Px));
然后到_Resetp0
里看, 进行了一些初始化操作, _Ref_count_base* _Rx
是外面new出来, 然后传进来的
然后再进来看 _Ref_count
的构造里的次数, 那么构造里的次数应该怎么做呢
发现_Ref_count的构造 用的是_Ref_count_base()的构造函数
然后回到_Ref_count_base的构造函数, 按道理, 父类的构造函数没什么好看的
发现父类_Ref_count_base(), 直接将两个引用计数初始化为1
为了让我们支持多线程操作, 这个操作是一个原子操作
总结:
首先要初始化父类的指针, 会把内部_Ux*_Px
指针传进来
Resetup0, 对第1个参数内部指针的操作只是简单的赋值
第2个参数new _Ref_count<_Ux>(_Px));
, 是要把内部的计数器更新下
_Ref_count类没进行什么操作, 关键在它的父类 _Ref_count_base
父类_Ref_count_base
里, 分别对 _Uses
和_Weaks
赋值为1
强指针直接构造(拿原始指针构造)时,
1.初始化_Ty* _Ptr
2.创建_Ref_count对象
3. _Ref_count_base对象构造时, 会分别会为_Uses = 1(因为对象有人在使用, 所以赋值为1), 并且_Weaks = 1(_Weaks = 1是为了统计方案, 赋值为1, 也表达出有一个人在使用对象)
然后Weaks 1 - 1 = 0(-1后的值, 才表示外界有多少弱指针在使用)
计数器加减规则
2.另外一个对象通过拷贝构造, =赋值给新的shared_ptr, 那么内部指针计数器+1
因为内部对象有新的人在使用它, 自身计数器不变是因为没有弱指针
share给weak, 内部指针不变, 因为外界的shared_ptr表示的内部计数器没有变化, 而weak多了一个, 自身引用计数器+1, 表示外界有新的人在使用
从weak中获取一个shared_ptr, 内部计数器+1, 自身不变
减少也是类似, 当shared_ptr析构的时候, 内部计数器-1, 当内部计数器减少为0的时候, 则释放内部对象, 同时会将自身计数器(弱指针计数器)-1
如果引用计数器减为了0, 就将引用计数器(Ref)释放掉
同理Weak在析构的时候, 只减自身的计数器, 自身计数器减为0, 就释放_Ref_count
对象