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

C++——shared_ptr:make_shared的用处,与shared_ptr直接构造的区别

shared_ptr

shared_ptr继承自__shared_ptr,其中有两个对象,一个是指向资源的指针,一个是控制块,指向一个引用计数对象。控制块中存储了强引用和弱引用的计数,强引用Uses代表shared_ptr对象的引用计数,弱引用Weaks代表weak_ptr对象的引用计数。
在这里插入图片描述
大概结构如上图所示,控制块其中也存储了指向资源的指针。
因此在构造一个shared_ptr对象的时候,会有两次堆分配,一次是为资源分配,一次是为控制块分配。因为每一个指向这份资源的指针对象都需要看到同一份引用计数,因此跟资源一样也是堆分配的。多次的堆分配和释放也就代表效率上的损失,而且极易产生内存碎片。

make_shared

C++11同时提供了make_shared函数,这是通过构造一个shared_ptr对象,而这个对象会事先申请一块足够大的内存空间,用于存放管理的资源以及控制块。即分配的堆空间是连续的,因此只有一次堆内存分配。

在这里插入图片描述
内存的结构就从左边的构造shared_ptr对象到右边的重构对象资源指针和引用计数。
相比shared_ptr构造,减少一次内存分配,提高效率,并且内存空间连续,减少内存碎片产生。但是,make_shared也存在缺点。

make_shared的缺点

自定义deleter

make_shared在构造智能指针对象的时候不能自定义deleter。在创建对象时同时创建控制块,这个控制块内部包含了引用计数、deleter等与管理资源相关的信息。因为资源和控制块是属于同一块申请的内存,所以使用自定义deleter可能会导致控制块内存被不正确地释放。因此,如果要使用deleter,应该使用shared_ptr直接构造。

构造函数

因为make_shared需要用到类的拷贝构造,因此需要被管理的类的构造函数是public的。

内存延迟归还

因为分配的空间是连续的,在资源指针的Uses变为0之后,控制块伴随资源的资源不会被立即释放,要等Weak也变为0,整块内存才被释放。资源只是被clear,但是但是没有归还操作系统。而如果是默认的控制块,在资源指针的Uses变为0之后,资源会被立即释放,内存立即归还。

通过调试看直接构造和make_shared的区别

void test2()
{std::shared_ptr<string> p1 = std::make_shared<std::string>(10, '9');{std::weak_ptr<std::string> wptr1;wptr1 = p1;std::shared_ptr<string> p2 = std::make_shared<std::string>("Hello");wptr1 = p2;p2 = p1;}std::cout << "end";
}void test1()
{std::shared_ptr<string> p1 = std::shared_ptr<std::string>(new std::string(10, '9'));{std::weak_ptr<std::string> wptr1;wptr1 = p1;std::shared_ptr<string> p2 = std::shared_ptr<std::string>(new std::string("Hello"));wptr1 = p2;p2 = p1;}std::cout << "end";
}int main()
{test1();test2();return 0;
}

直接构造在这里插入图片描述

当wptr指向p1的时候,可以看到p1的Weaks变为了2,weak_ptr观察到的内容与p1一致。并且注意此时control block的value显示为default,表示默认的控制块。
在这里插入图片描述
当weak_ptr指向p2,并且将p2指向p1,意思就是p2原来管理的Hello资源要释放掉,然后用p1拷贝构造一个对象,赋值给p2,让p1和p2同时管理10个9。
此时可以看到weak_ptr的资源指针已经显示Error reading,说明资源已经释放,内存已经归还了。

make_shared构造

在这里插入图片描述
此时可以看到原来为default的control block已经改为了make_shared。
在这里插入图片描述
重复之前的操作,把p2指向p1,再看weak_ptr的成员。可以看到ptr指向的资源并没有被释放,只是内容并清空而已。只有当weak_ptr的生命周期结束,整个内存块才会被释放,归还给操作系统。
这就是make_shared最主要的缺点,在某些内存要求高的场景下可能不太适用。

http://www.lryc.cn/news/149406.html

相关文章:

  • 【网络安全带你练爬虫-100练】第17练:分割字符串
  • Unity 之ToolTip的用法
  • xsschallenge通关(11-15)
  • Kubernetes技术--k8s核心技术集群的安全机制RBAC
  • 【JavaSE】String类
  • DBMS Scheduler设置重复间隔
  • windows的redis配置sentinel
  • NetMarvel机器学习促广告收益最大化,加速获客
  • Spring-5.0.x源码下载及本地环境搭建
  • go中的切片
  • C++笔记之单例通过GetInstance传递参数
  • 1688API技术解析,实现获得1688商品详情
  • 【Java 动态数据统计图】动态X轴二级数据统计图思路Demo(动态,排序,动态数组(重点推荐:难)九(131)
  • C#将text文本中的单双行分开单独保存
  • 深入理解 Go 语言中的 iota
  • 【力扣】55、跳跃游戏
  • 个人与公司合作,怎么代开发票?有哪些优惠政策?
  • 什么是计算机视觉,计算机视觉的主要任务及应用
  • 网易24届内推
  • redis 应用 4: HyperLogLog
  • 进程的挂起状态
  • idea 链接mysql连不上
  • Ubuntu 启动出现grub rescue
  • go中runtime包里面的mutex是什么?runtime.mutex解析
  • VScode 调试python程序,debug状态闪断问题的解决方法
  • 飞桨中的李宏毅课程中的第一个项目——PM2.5的预测
  • Qt---对话框 事件处理 如何发布自己写的软件
  • 【C++】C++ 引用详解 ⑩ ( 常量引用案例 )
  • React原理 - React Reconciliation-下
  • YOLOv8超参数调优教程! 使用Ray Tune进行高效的超参数调优!