C++新特性21_shared_ptr与weak_ptr(大量使用,仅看此篇即可;使用方法;注意事项:不能用同一指针去初始化两个shared_ptr;循环引用问题;weak_pt用于解决循环引用问题)

2023-11-11

前面介绍了智能指针中早期的两个版本:auto_ptr(已废弃),unique_ptr(不常用,auto_ptr的升级,限制了某些操作避免了一些问题),本篇介绍在开发中真正可能被大量使用的指针shared_ptr。weak_ptr是对shared_ptr的一种补充

1. shared_ptr是带引用计数的智能指针。

1.1 shared_ptr构造

其初始化多了一种写法:std::make_shared

void foo_construct()
{
	int* p = new int(3);//创建int指针

	std::shared_ptr<int> sptr(p);
	std::shared_ptr<int> sptr2(new int(4)); //更建议的初始化方法
	std::shared_ptr<int> sptr3 = sptr2;//拷贝构造 实质是带有引用计数的指针
	std::shared_ptr<int> sptr4 = std::make_shared<int>(5);
}

在这里插入图片描述
这里显然可以看到有引用计数的存在。

通过修改上面例子中的sptr3的作用域,可以发现,出了块作用域之后,shared_ptr对应的引用计数的值减少了。

void foo_construct()
{
		std::shared_ptr<int> sptr2(new int(4));
		{
			std::shared_ptr<int> sptr3 = sptr2;
		}
}

在这里插入图片描述

1.2 shared_ptr注意事项:

1.2.1 如果用同一个指针去初始化两个shared_ptr时,则引用计数仍然会出错:

void foo_test()
{
	int* p = new int(3);
	{
	    //出外层的块作用域又会析构p,这个时候就会重复析构p
		std::shared_ptr<int> sptr(p);
		{
		    //出块作用域就会析构p
			std::shared_ptr<int> sptr2(p); 
		}
	}
}

在这里插入图片描述
参考笔记中记录的过程:
在这里插入图片描述
显然出了最里面的作用域之后,sptr2对象就已经释放了,此时,对于sptr2来说,p的引用计数为0,所有p被释放,但是实际上sptr还存在,所以再释放sptr时,就会0xc0000005。
在这里插入图片描述

因此标准的构造过程,应按照以下方式:

void foo_construct()
{
        //资源的申请只能在构造的时候按照以下方式写
		std::shared_ptr<int> sptr2(new int(4));
		{
		//任何其他的智能指针的初始化只能通过其他智能指针进行
			std::shared_ptr<int> sptr3 = sptr2;
		}
}

1.2.2 为什么需要weak_ptr?:避免循环引用的问题

shared_ptr最大的问题是存在循环引用的问题:两个类同时包含了另一个类的智能指针,互相引用的情况,再进行析构时会出现异常

如果两个类的原始指针的循环使用,那么会出现重复释放的问题:


#include <iostream>
#include <memory>

using namespace std;

class CPerson;
class CSon;

//循环引用的问题
//两个类同时包含了另一个类的智能指针,互相引用
class Cperson
{
public:
	Cperson() {

	}

	//调用方法将外部的智能指针进行赋值
	void Set(std::shared_ptr<CSon> pSon) {
		m_pSon = pSon;
	}

	~Cperson() {
	}

	//包含另外一个儿子类的智能指针
	std::shared_ptr<CSon> m_pSon; 
};

class CSon
{
public:
	CSon() {

	}

	void Set(std::shared_ptr<Cperson> pParent) {
		m_pParent = pParent;
	}

	~CSon() {
	}
	//包含父类的智能指针
	std::shared_ptr<Cperson> m_pParent;
};

void testShared()
{
	CSon* pSon = new CSon();
	Cperson* pPer = new Cperson();

	{
		std::shared_ptr<Cperson> shared_Parent(pPer); //利用父亲的对象定义父亲的智能指针,父类智能指针的引用计数+1
		std::shared_ptr<CSon> shared_Son(pSon); //利用儿子的对象定义儿子的智能指针

		shared_Parent->Set(shared_Son); //父亲类对象调用儿子的智能指针方法
		shared_Son->Set(shared_Parent); //调用的方法中shared_Parent赋值给子类中的m_pParent,父类的引用计数+1,此处自己感觉应该是pSon->(0607),下篇是原理解析可以印证

		//打印出使用次数
		printf("pSon : use_count = %d\r\n", shared_Son.use_count());
		printf("pPer : use_count = %d\r\n", shared_Parent.use_count());
	}


}

int main(int argc, char* argv[])
{
	testShared();

	return 0;
}

出了作用域,按道理应该释放,但是此时对象并未析构
在这里插入图片描述
在这里插入图片描述
最后两者的引用计数均为1,原因是出了块作用域之后,两个shared_parent和shared_son均会析构,在这两个智能指针的内部,均会先去判断对应的内部指针-1是否为0,显然这里相互引用的情况下,引用计数初值为2,减1后值为1,所以两个指针均不会被释放。

这里,其实只需要一个释放了,另外一个也能跟着释放,可以采用弱指针,即人为的迫使其中一个引用计数为1,从而打破闭环。

这里只需要将上例子中的任意一个强指针改为弱指针即可。
在这里插入图片描述
此时,两个内部指针均会得到释放。
在这里插入图片描述
原因是,弱指针的引用不会增加原来的引用计数,那么就使得引用不再是闭环,所以在出作用域之后,全部得到释放。

2. weak_ptr的使用

  • weak_ptr本身并不具有普通内部指针的功能,而只是用来观察其对应的强指针的使用次数。

  • 因此,这里弱指针的在使用上,实际上是一个特例,即不增加引用计数也能获取对象,因此,实际上在使用弱指针时,不能通过弱指针,直接访问内部指针的数据,而应该是先判断该弱指针所观察的强指针是否存在(调用expired()函数),如果存在,那么则使用lock()函数来获取一个新的shared_ptr来使用对应的内部指针。

  • 实际上,如果不存在循环引用,就不需要使用weak_ptr了,这种做法仍然增加了程序员的负担,所以不如java c#等语言垃圾回收机制省心。

3. 学习视频地址:shared_ptr与weak_ptr

4.学习笔记:shared_ptr与weak_ptr笔记

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++新特性21_shared_ptr与weak_ptr(大量使用,仅看此篇即可;使用方法;注意事项:不能用同一指针去初始化两个shared_ptr;循环引用问题;weak_pt用于解决循环引用问题) 的相关文章

随机推荐

  • 线性代数(python)

    线性代数模块 概念 线性代数是数学运算中的一个重要工具 它在图形信号处理 音频信号处理中起非常重要的作用 numpy linalg模块中有一组标准的矩阵分解运算以及诸如逆和行列式之类的东西 例如 矩阵相乘 如果我们通过 对两个数组相乘的话
  • vue-admin-template中左上角logo让其显示、修改为自定义图片

    目录 显示logo 修改logo 显示logo 1 找到settings js文件 找到sidebarLogo 侧边栏logo 2 修改sidebarLogo的值为true 修改logo 1 找到src layout components
  • WEB前端网页设计-Bootstrap5 弹出框 & JavaScript 函数

    目录 Bootstrap5 弹出框 如何创建弹出框 指定弹出框的位置 关闭弹出框 JavaScript 函数 JavaScript 函数语法 调用带参数的函数 带有返回值的函数 语法 局部 JavaScript 变量 全局 JavaScri
  • TensorBoard 启动protobuf报错

    libprotobuf ERROR external protobuf archive src google protobuf descriptor database cc 334 Invalid file descriptor data
  • c语言成绩管理系统

    关注微信公众号每日新觉 私聊作者获取提取密码点击下面链接 私聊作者获取提取密码 https mp weixin qq com s QgRQ3YB2JqCvNOGQHQ57rA 作者每日新觉 是一名热爱技术和编程的年轻程序员 他在计算机科学和
  • js中,清空对象(删除对象的属性)

    在项目中 有些对象用完后需要重置 下面简单介绍下JS中清除对象的方法 方法如下 方法一 字面量定义对象 第一步 定义一个空对象并打印出来 代码和效果 代码 const student console log student 打印结果 第二步
  • 二进制ASCII转换(KeilC+Proteus) 跟书学程序系列

    给定的一个字节二进制数 转换成ASCII码 将累加器A中的值拆为两个ASCII码 并存入从RWESULT开始的两个单元中 示例程序给A赋值 1AH 代码如下 RESULT EQU 30H ORG 00HSTART MOV A 1AH CAL
  • Linux安装失败Grub-install问题超详细解决,傻瓜教学!!

    先说一下我安装Linux的过程 这里只是我无力地吐槽不想看直接往后跳 一年前接触Linux 软件安装GJ公众号上看到的 然后在虚拟机上跑 最近认识了一个大佬 双 爽 系统Ubuntu18 04 win10 突发奇想我也来装个双系统 双系统就
  • 常用端口对照表

    TCP 端口 静态端口 端口类型 端口号 注释 TCP 0 Reserved TCP 1 TCP Port Service Multiplexer TCP 2 Death TCP 5 Remote Job Entry yoyo TCP 7
  • Windows环境下利用Wget批量下载Earthdata数据

    使用wget下载Earthdata数据 首先是下载wget exe 然后保存在自己预设的目录中 同时将Earthdata上获取的download txt也放在该目录中 想要批量下载的数据的路径就保存在该txt文件中 接着win R打开cmd
  • js如何将选中图片文件转换成Base64字符串?

    如何将input type file 选中的文件转换成Base64的字符串呢 1 首先了解一下为什么要把图片文件转换成Base64的字符串 在常规的web开发过程中 大部分上传文件都是在web页面端通过表单直接提交 再由服务器端捕获请求来进
  • HBuilderX连接MuMu模拟器

    1 打开MuMu多开器 启动MuMu模拟器 然后点击ADB查看模拟器的端口号 2 在HBuilderX中配置模拟器端口号和adb路径 以自己的实际路径为准 3 配置环境变量 因为需要运行adb命令 所以需要配置环境变量 在MuMu的安装路径
  • 关于Android Studio检测不到模拟器/真机

    问题表现 初始化ADB不成功 无法连接到ADB unable to establish a connection to adb 打开选择设备界面 检测不到任何模拟器or真机 或许还有其他问题 通常的解决方法 按照网上大部分流传的 查看是否是
  • ES2015 Iterable(可迭代的)接口

    一 为什么ES2015提供Iterable 可迭代的 接口 ES中能够表示有结构的数据类型越来越多 以前有数组 对象等等 ES2015中新增了Map和Set对象 为了给各种各样的数据结构提供统一遍历方式 ES2015提供了Iterable
  • C语言--两数之和

    两数之和 给定一个整数数组 nums 和一个目标值 target 请你在该数组中找出和为目标值的那 两个 整数 并返回他们的数组下标 你可以假设每种输入只会对应一个答案 但是 你不能重复利用这个数组中同样的元素 示例 给定 nums 2 7
  • 前端后端的区别(超详细版)

    前端与后端开发中技术差异的全面对比 什么是前端开发 网站和移动应用的前端 物联网的前端 什么是后端开发 网站和移动应用后端 物联网后端 前端 Vs 后端有什么区别 前端与后端技术栈 前端 Vs 后端 谁是最好的 前端与后端开发人员 前端 V
  • docker安装rabbitmq

    启动脚本 docker stop rabbitmq docker rm rabbitmq docker run name rabbitmq restart always p 5670 5670 p 15670 15670 p 5674 56
  • 英文时间点的表达

    1 所有时间都可以用 小时 分钟 直接读 8 30 eight thirty 2 40 two forty 2 时间在半小时之内的 可以用 分钟 past 小时 6 10 ten past six 4 20 twenty past four
  • springboot使用aop进行全局事务管理

    依赖
  • C++新特性21_shared_ptr与weak_ptr(大量使用,仅看此篇即可;使用方法;注意事项:不能用同一指针去初始化两个shared_ptr;循环引用问题;weak_pt用于解决循环引用问题)

    前面介绍了智能指针中早期的两个版本 auto ptr 已废弃 unique ptr 不常用 auto ptr的升级 限制了某些操作避免了一些问题 本篇介绍在开发中真正可能被大量使用的指针shared ptr weak ptr是对shared