C++RAII内存管理技术

在这里插入图片描述

文章目录

  • 一.什么是RAII内存管理技术?
  • 二.智能指针
    • unique_ptr
    • shared_ptr
    • 循环引用问题
    • weak_ptr

一.什么是RAII内存管理技术?

  • C++在引入异常机制后,代码执行流的跳转变得难以预料,如果使用普通的指针进行内存管理,很难避免内存泄漏的问题(执行流跳转导致堆区资源无法被释放)
  • RAII技术指的是利用对象的生命周期来管理内存资源,就堆区内存资源的管理而言,指的就是:将指针封装在类中,在类对象构造时获取堆区资源,当类对象生命周期结束时,通过类对象的析构函数自动完成堆区资源的释放,这样的类对象就是智能指针
    • 智能指针可以有效地避免开发中出现内存泄漏的问题,同时为开发者省去了很多时间和精力

二.智能指针

  • C++11标准库中智能指针主要分为三大类:在这里插入图片描述

unique_ptr

  • unique_ptr对象之间不允许进行拷贝,即一个unique_ptr对象管理一块堆区内存资源,是一一对应的关系
  • unique_ptr的简单实现原理:
	//不允许拷贝的智能指针
	//delfunc是堆区资源释放函数,用于调用delete或者delete[]
	template<class T,class delfunc>
	class unique_ptr
	{
	public:
		unique_ptr(T * ptr = nullptr)
			:_ptr(ptr)
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				delfunc del;
				del(_ptr);
				std::cout << "delete" << std::endl;
			}
		}


		//让智能指针可以像指针一样被使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		//禁止unique_ptr对象间的拷贝
		unique_ptr(const unique_ptr<T, delfunc>& unptr) = delete;
		unique_ptr<T, delfunc>& operator=(const unique_ptr<T, delfunc>& unptr) = delete;
	private:
		T* _ptr;
	};
  • 使用指针管理堆区资源时,我们会让其指向单个对象或者对象数组,单个对象delete完成资源释放,而对象数组要采用delete[]完成资源释放.unique_ptr的类模板参数delfunc的设计目的就是为了区分上述两种情况,让使用者通过设计仿函数的方式在deletedelete[]之间做选择

shared_ptr

  • shared_ptr是允许进行拷贝的智能指针,然而shared_ptr对象之间发生拷贝时,就会出现多个智能指针对象同时管理同一块堆区内存的情况,shared_ptr通过引用计数的技术避免了同一块堆区资源被释放多次的情况出现:在这里插入图片描述
  • 引用计数的实现思路:
    • shared_ptr内部封装一个指针int * recount用来维护引用计数变量
    • 每当一个shared_ptr对象进行构造并申请堆区资源时,同时在堆区申请一个int变量作为该shared_ptr对象所指向的堆区资源的引用计数变量
    • 每当shared_ptr被拷贝时,连同int * recount一起拷贝,然后让引用计数变量加一即可(赋值重载的实现还要考虑引用计数变量减一和堆区资源释放的问题)
    • 每当shared_ptr析构时,引用计数变量减一,若引用计数变量减为0,则释放堆区资源在这里插入图片描述
  • shared_ptr的简单实现原理:
    //允许拷贝的智能指针,利用引用计数解决内存管理冲突
	//delfunc是堆区资源释放函数,用于调用delete或者delete[]
	template<class T, class delfunc>
	class shared_ptr
	{
	public:
		//构造智能指针(同时创建引用计数变量)
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr),
			 _recount(new int(1))
		{}
		shared_ptr(const shared_ptr<T, delfunc>& shptr)
			: _ptr(shptr._ptr),
			  _recount(shptr._recount)
		{
			//智能指针拷贝增加引用计数
			(*_recount)++;
		}
		shared_ptr<T, delfunc>& operator=(const shared_ptr<T, delfunc>& shptr)
		{
			if (_ptr != shptr._ptr)
			{
				//智能指针指向改变,其先前指向的堆区内存和引用计数变量需要处理
				Release();
				//完成智能指针拷贝并增加引用计数
				_ptr = shptr._ptr;
				_recount = shptr._recount;
				++(*_recount);
			}
			return (*this);
		}
		~shared_ptr()
		{
			Release();
		}


		//让智能指针可以像指针一样被使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
		T* getptr() const
		{
			return _ptr;
		}
	private:
		//将资源释放函数进行封装,方便复用
		//引用计数减一,若引用计数减为0则释放资源
		void Release()
		{
			//引用计数减一
			--(*_recount);
			//引用计数为0则释放资源
			if (*_recount == 0)
			{
				if (_ptr)
				{
					delfunc del;
					del(_ptr);
					std::cout << "delete" << std::endl;
				}
				//同时注意释放引用计数变量
				delete _recount;
			}
		}
	private:
		T* _ptr;
		int* _recount;
	};
  • shared_ptr类内部同时还要解决引用计数变量带来的线程安全的问题,这里暂不讨论

循环引用问题

  • shared_ptr用在自引用结构体中会出现对象无法析构的问题:

template<class T>
class deletefunc
{
public:
	void operator()(T* ptr)
	{
		delete  ptr;
	}
};
// 循环引用
struct ListNode
{
	int _val;
	shared_ptr<ListNode,deletefunc<ListNode>> _next;
	shared_ptr<ListNode,deletefunc<ListNode>> _prev;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

// 循环引用
void test_shared_cycle()
{
	share_ptr<ListNode, deletefunc<ListNode>> n1(new ListNode);
	share_ptr<ListNode, deletefunc<ListNode>> n2(new ListNode);
	n1->_next = n2;
	n2->_prev = n1;
}
  • test_shared_cycle()函数执行完后,两个链表节点都无法正常析构,shared_ptr引发了循环引用的问题:在这里插入图片描述
  • n1节点的析构依赖于n2节点中_prev指针的析构,n2节点中_prev指针的析构依赖于n2节点的析构,n2节点的析构又依赖于n1节点中_next指针的析构, n1节点中_next指针的析构又依赖于n1节点的析构.如此往复构成了循环的依赖关系导致两个节点都无法被释放.
  • weak_ptr就是为了解决循环引用问题而设计的

weak_ptr

  • weak_ptr智能指针不参与堆区内存的申请和释放,也不会增加特定内存块的引用计数(其功能和普通指针差不多),weak_ptr支持以shared_ptr为引用形参的拷贝构造和赋值
  • weak_ptr简单实现原理:
	//用于配合share_ptr的使用用于循环引用的场景
	//delfunc是堆区资源释放函数,用于调用delete或者delete[]
	//weak_ptr不参与资源的申请和释放
	template<class T, class delfunc>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		~weak_ptr()
		{}
		
		weak_ptr(const weak_ptr<T, delfunc>& wptr)
			:_ptr(wptr._ptr)
		{}
		weak_ptr(const share_ptr<T, delfunc>& shptr)
			:_ptr(shptr.getptr())
		{}
		weak_ptr<T, delfunc>& operator=(const weak_ptr<T, delfunc>& wptr)
		{
			_ptr = wptr._ptr;
			return (*this);
		}
		weak_ptr<T, delfunc>& operator=(const share_ptr<T, delfunc>& shptr)
		{
			_ptr = shptr.getptr();
			return (*this);
		}

		//让智能指针可以像指针一样被使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
  • 自引用结构体中采用weak_ptr就可以避免出现循环引用的问题
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/75303.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java 集合框架API接口】Collection,List,Set,Map,Queue,Deque

博主&#xff1a;_LJaXi Or 東方幻想郷 专栏&#xff1a; Java | 从跨行业到跨平台 开发工具&#xff1a;IntelliJ IDEA 2021.1.3 Java集合框架 API接口 Collection接口List接口HashSet&#xff0c; TreeSetSet接口使用 HashSet 实现使用 TreeSet 实现 HashMap、TreeMapMap接口…

MySQL高可用MHA

目录 前言 一、概述 二、配置免密、组从复制 三、MHA配置 四、测试 总结 前言 MySQL高可用管理工具&#xff08;MHA&#xff0c;Master High Availability&#xff09;是一个用于自动管理MySQL主从复制的工具&#xff0c;它可以提供高可用性和自动故障转移。MHA由原版的MHA工具…

Debian/Ubuntu清理硬盘空间

Debian/Ubuntu清理硬盘空间_debian清理磁盘空间_weixin_43606319的博客-CSDN博客 1. 删除残余的配置文件 通常Debian/Ubuntu删除软件包可以用两条命令 sudo apt-get remove <package-name> sudo apt-get purge <package-name> remove将会删除软件包&#xff0…

1.SpringMVC接收请求参数及数据回显:前端url地址栏传递参数通过转发显示在网页

1、SpringMVC 处理前端提交的数据 1.1 提交的域名和处理方法的参数不一致&#xff0c;使用注解解决 1.2 提交的域名和处理方法的参数不一致&#xff0c;使用注解解决 1.3 提交的是一个对象 2、前端url地址栏传递的是一个参数 请求地址url&#xff1a;http://localhost:8080/s…

5_docker-compose多服务统一编排管理

系列文章目录 第1章 Linux安装Docker 第2章 Docker安装jdk1.8和MySql 第3章 Docker安装redis 第4章 Jar包部署Docker 第5章 Docker-compose多服务统一编排管理 第6章 AccessKeyId和AccessKeySecret的环境变量配置 文章目录 系列文章目录前言 一、编写docker-compose.yml二、上…

计算机网络核心-数据交换

1 概述 计算机网络的核心即数据交换。通过数据交换将数据从源主机发送到目的主机。 2 为什么需要数据交换 如果不是数据交换的方式&#xff0c;而是每两台主机直接连接&#xff0c;则会产生N^2链路问题。 即&#xff0c;假设有N台主机&#xff0c;两两间建立连接&#xff0c…

软件测试基础篇——Linux

1、Linux系统的特征 开源免费&#xff1a; 开源&#xff1a;开放源代码&#xff0c;指的是底层的源代码是可以开放出来&#xff0c;给相关的开发者&#xff0c;根据实际的需求做出修改的。 免费&#xff1a;不花钱&#xff0c;自由传播。 ​ Linux是一种免费使用和自由传播的…

pytest数据驱动(最简单)

目录 第一种&#xff1a;通过yaml文件获取数据&#xff08;一维列表&#xff09; 第二种&#xff1a;通过yaml文件获取数据&#xff08;二维列表&#xff09; 第三种&#xff1a;通过yaml文件获取数据&#xff08;pytest.fixture&#xff09; 资料获取方法 第一种&#xff…

RTC实验

一、RTC简介 RTC(Real Time Clock)即实时时钟&#xff0c;它是一个可以为系统提供精确的时间基准的元器件&#xff0c;RTC一般采用精度较高的晶振作为时钟源&#xff0c;有些RTC为了在主电源掉电时还可以工作&#xff0c;需要外加电池供电BCD码&#xff0c;四位二进制表示一位…

【潮州饶平】联想 IBM x3850 x6 io主板故障 服务器维修

哈喽 最近比较忙也好久没有更新服务器维修案例了&#xff0c;这次分享一例潮州市饶平县某企业工厂一台IBM System x3850 x6服务器亮黄灯告警且无法正常开机的服务器故障问题。潮州饶平ibm服务器维修IO主板故障问题 故障如下图所示&#xff1a; 故障服务器型号&#xff1a;IBM 或…

CI/CD流水线实战

不知道为什么&#xff0c;现在什么技术都想学&#xff0c;因为我觉得我遇到了技术的壁垒&#xff0c;大的项目接触不到&#xff0c;做的项目一个字辣*。所以&#xff0c;整个人心浮气躁&#xff0c;我已经得通过每天的骑行和长跑缓解这种浮躁了。一个周末&#xff0c;我再次宅在…

2011-2021年数字普惠金融指数Bartik工具变量法(含原始数据和Bartik工具变量法代码)

2011-2021年数字普惠金融指数Bartik工具变量法&#xff08;含原始数据和Bartik工具变量法代码&#xff09; 1、时间&#xff1a;2011-2020&#xff08;省级、城市&#xff09;&#xff0c;2014-2020&#xff08;区县&#xff09; 2、原始数据来源&#xff1a;北大金融研究中心…

【数据结构】双链表

【数据结构】双链表 一. 前言二. 带头双向链表接口实现1.准备工作2. 创建一个节点 三. 初始化4. 打印5. 尾插6. 尾删7. 头插8. 头删9. 计算节点个数10. 查找数据11. 在任意位置插入数据12. 在任意位置删除数据13. 销毁 四. 如何10分钟内完成一个完整双链表 一. 前言 带头双向循…

通达OA SQL注入漏洞【CVE-2023-4165】

通达OA SQL注入漏洞【CVE-2023-4165】 一、产品简介二、漏洞概述三、影响范围四、复现环境POC小龙POC检测工具: 五、修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损…

元宇宙时代超高清视音频技术白皮书关于流媒体协议和媒体传输解读

流媒体协议 元宇宙业务场景对流媒体传输的实时性和互动性提出了更高的要求&#xff0c;这就需要在传统的 RTMP、SRT、 HLS 等基础上增加实时互动的支持。实时互动&#xff0c;指在远程条件下沟通、协作&#xff0c;可随时随地接入、实时地传递虚实融合的多维信息&#xff0c;身…

uni-app日期选择器

写个简单的日期选择器&#xff0c;还没搞样式&#xff0c;所以有点丑 大概长这样吧 首先是这个picker选择器&#xff0c;mode选择日期&#xff0c;end是写一个范围前日期&#xff0c;:end就是这个日期是动态变化的&#xff0c;还有change函数 <template><view>&l…

Mybatis 初识

目录 1. MyBatis入门 1.1 MyBatis的定义 1.2 MyBatis的核心 MyBatis的核心 JDBC 的操作回顾 1.3 MyBatis的执行流程 MyBatis基本工作原理 2. MyBatis的使用 2.1 MyBatis环境搭建 2.1.1 创建数据库和表 2.1.2 添加MyBatis框架支持 老项目添加MyBatis 新项目添加MyBatis 2.1.3 设…

iphone拷贝照片中间带E自动去重软件,以及java程序如何打包成jar和exe

文章目录 一、前提二、问题描述三、原始处理方式四、程序处理4.1 java程序如何打包exe4.1.1 首先打包jar4.1.2 开始生成exe4.1.3 软件使用方式 4.2 更换图标4.2.1 更换swing的打包jar图标4.2.2 更换exe图标 4.3 如何使生成的exe在没有java环境的电脑上运行4.3.1 Inno Setup打包…

uniapp的uview-plus组件库的导入

uniapp的vue3中使用uview-plus组件库。在插件市场中找到该组件并点击如下所示绿色按钮&#xff0c;弹出弹窗选择要导入的项目后&#xff0c;就会在uni_modules文件中生成如下文件内容 关于插件的下载区别&#xff0c;可参考&#xff1a;https://uniapp.dcloud.net.cn/compone…

VSCode如何设置高亮

一、概述 本文主要介绍在 VSCode 看代码时&#xff0c;怎样使某个单词高亮显示&#xff0c;主要通过以下三步实现&#xff1a; 安装 highlight-words 插件 配置 highlight-words 插件 设置高亮快捷键F8 工作是嵌入式开发的&#xff0c;代码主要是C/C的&#xff0c;之前一直用…