shellmad-24_C++新特性 share_ptr与weak_ptr的源码分析

深入分析shared_ptr与weak_ptr的实现

stl中使用了shared_ptr来管理一个对象的内部指针,并且使用了weak_ptr来防止前面所提到的shared_ptr循环引用的问题。

接下来简单的分析shared_ptr和weak_ptr的实现,最后通过自己写代码来模拟shared_ptr和weak_ptr,达到深入学习的目的:

测试代码如下:

#include "stdafx.h"
#include <memory>

int _tmain(int argc, _TCHAR* argv[])
{   
    std::shared_ptr<int> sptr(new int(3));
    std::shared_ptr<int> sptr2 = sptr;

    std::weak_ptr<int> wptr = sptr;

    if (!wptr.expired()){
        
        std::shared_ptr<int> sptr3 = wptr.lock();
    }

    return 0;
}

通过朗读源码可以发现
shared_ptr : 对外提供接口, 并无成员变量 表示强指针
父类: _Ptr_base
weak_ptr : 对外提供接口, 并无成员变量 表示弱指针
父类: _Ptr_base
显然次数的统计, 不在share_ptr里, 应该是在_Ptr_base里

再进到_Ptr_base里, 代码里说明了这是一个基类

在这里插入图片描述

看下父类的实现, 发现提供了一些基本的方法, 前面加_的表示内部的方法

在这里插入图片描述
注意找成员, 两个成员需要关注:
在这里插入图片描述

	_Ptr_base{
		两个成员:
		_Ty *_Ptr;
		_Ref_count_base *_Rep;
	}

注意, 这两个成员都是指针, 并且在前面的代码中, 也很相似(16.为智能指针添加模板.md)
提供了引用次数的管理
在这里插入图片描述
引用次数里有原始的指针, 叫做内部指针, 这个指针表达的原始的指针内容是什么, 就是真正的资源的类型
在这里插入图片描述
接下来还有个管理引用次数的, 我们封装成了一个类
在这里插入图片描述

所以代码很相似

	_Ptr_base{
		两个成员:
		_Ty *_Ptr; // 表达智能指针关联的原始指针 内部指针
		_Ref_count_base *_Rep; // 用于管理智能指针的次数 
	}

_Ty *_Ptr;内部的类型, 不用说了, 是跟以前一样, 模板类型
_Ref_count_base是一个次数的类, 所以看这个类

在这里插入图片描述
首先有两个虚函数, 而且是纯虚函数, =0表示纯虚函数, 而且_Ref_count_base并没有父类

	_Ptr_base{
		两个成员:
		_Ty *_Ptr; // 表达智能指针关联的原始指针 内部指针
		_Ref_count_base *_Rep; // 用于管理智能指针的次数 
	}
	基类 纯虚类
	_Ref_count_base {
		 virtual void _Destroy() _NOEXCEPT = 0;
         virtual void _Delete_this() _NOEXCEPT = 0;
	}

重头戏: 成员变量
发现好东西,
强指针计数器
弱指针计数器

在这里插入图片描述

_Atomic_counter_t类型不熟悉, 翻过去看下, 是个重定义
在这里插入图片描述

在这里插入图片描述
发现是个int类型, 无符号整形 , 4字节

	_Ptr_base{
		两个成员:
		_Ty *_Ptr; // 表达智能指针关联的原始指针 内部指针
		_Ref_count_base *_Rep; // 用于管理智能指针的次数 
	}
	基类 纯虚类
	_Ref_count_base {
		 virtual void _Destroy() _NOEXCEPT = 0;
         virtual void _Delete_this() _NOEXCEPT = 0;
         // 实际上表达的是当前有多少个强指针在引用内部指针
		 _Atomic_counter_t _Uses;  // 表示强指针的引用次数 
		
		// 实际上表达的是当前_Ref_count_base类型的使用次数
		 _Atomic_counter_t _Weaks; // 表示弱指针的使用次数
	}

弱指针, 弱指针, 表达的是弱指针的引用次数, 更关键的是, 它是_Ref_count_base对象的计数器

也就是说弱指针的计数器减为了0, 那么表示我们要释放_Ref_count_base *_Rep;对象

那为什么是纯虚基类呢?
纯虚基类, 是不能有对象的, 不能new的
很显然, *_Rep需要new出来

因此_Ref_count_base必须有一个派生类
往下拉, 发现_Ref_count
在这里插入图片描述
派生类:
_Ref_count : // 真正的计数器对象, 使用时, 需要将指针强转成父类指针, 仅仅使用接口

父类提供了纯虚函数, 子类就实现了纯虚函数

真正使用的时候, 需要将_Ref_count类强转为父类, 放到Ptr_base的成员变量_Ref_count_base* _Rep的地方, 就可以了
在这里插入图片描述

_Ref_count:{
// 真正的计数器对象, 使用时, 需要将指针强转成父类指针, 仅仅使用接口

// 派生类多了一个成员
	_Ty* _Ptr; // 表达是内部指针 

}

总的关系:
强指针/弱指针都有一个父类 _Ptr_base
父类_Ptr_base成员里, 有一个_Ty 表达真正的指针, 另外_Ref_count_base一个表达计数器

计数器_Ref_count_base 是一个纯虚基类, 有两个计数器

然后有一个派生类, 对纯虚基类的函数进行了实现, 实现里面多个了
T_y* _Ptr指针


代码运行分析:

sptr:里面有个父类Ptr_Base
然后父类里有两个原始的成员, 一个是内部指针_Ptr, 一个是计数器_Rep

在这里插入图片描述
单步走一步, 发现原始内部成员, 就是我们申请的3
_Rep就是我们申请的计数器
在这里插入图片描述
然后计数器又很原始, 本质有3个东西, 首先有Uses, Weaks, 其次它还不够
在这里插入图片描述
红框表示强弱指针统计的次数, 那第1行表示什么呢, 因为是一个纯虚类, 从父类继承的子类, 父类有虚函数, 因此第1行是虚表
在这里插入图片描述
虚表里有两个函数

在这里插入图片描述
除此之外, 还有第4行, 小端存储, 因此要00 9e 79 50这样读
发现就是我们的3
就是我们刚才写的, 两个计数器 + 原始的内部指针
在这里插入图片描述
但是懂了实现, 还不够, 因为你还需要知道强指针/弱指针什么时候+1/-1; 什么时候减为0, 最后表示它能够释放

总结强弱指针框架

     shared_ptr :  对外提供接口,并无成员变量 表示强指针
               父类:_Ptr_base

     weak_ptr   :  对外提供接口,并无成员变量 表示弱指针
               父类:_Ptr_base

     _Ptr_base{
 
        两个成员变量:
            _Ty *_Ptr;    //表示智能指针关联的原始的指针, 内部指针
            _Ref_count_base *_Rep; //用于管理智能指针的次数
     }

     基类 纯虚类
     _Ref_count_base{
            virtual void _Destroy() _NOEXCEPT = 0;
            virtual void _Delete_this() _NOEXCEPT = 0;

            //实际上表达的是当前有多少个强指针在引用内部指针
            _Atomic_counter_t _Uses;  //表示强指针使用次数 

            //实际上表达的是当前_Ref_count_base类型的使用次数
            _Atomic_counter_t _Weaks; //表示弱指针使用次数
     }

     有一个派生类:
     _Ref_count: //真正的计数器对象,使用时,需要将指针强转为父类指针,仅仅使用接口
                _Ref_count_base
     {
        //派生类多了一个成员
        _Ty * _Ptr; //表达的是内部指针
     }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值