【C++】详细版 RAII技术的应用之智能指针(智能指针发展历程和简单模拟实现介绍)

news2025/1/12 6:04:35

 

目录

 

前言

一、智能指针有什么用?

二、什么是RAII(智能指针的底层思想)?

       三、智能指针的发展历程以及模拟实现

1.auto_ptr(C++98)

2.unique_ptr(C++11)

3.shared_ptr(C++11)


前言

C++中的智能指针是一种管理内存的工具,它可以自动地跟踪和管理所指向的内存块。智能指针通常用于替代手动管理内存的机制,避免内存泄漏和野指针等问题。


一、智能指针有什么用?

下面我们来看一种场景:

#include <iostream>
using namespace std;
double Division(int x, double y)
{
	cin >> x >> y;
	if (0 == y)
		throw invalid_argument("除数为0无法计算");
	return x / y;
}
void func()
{
	pair<int, string>* p1 = new pair<int, string>(7, "CSDN");
    int x;
    double y;
    cin >> x >> y;
	cout << Division(x, y) << endl;
	delete p1;
	p1 = nullptr;
}
int main()
{
	try
	{
		func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

从上面代码可以分析出:如果Dvision函数中抛异常的话,那么p1指向的空间内存就无法释放,造成内存泄露。因此此时就需要一个智能指针对p1指向的空间内存进行自动释放。因此我们可以这样做:利用对象的生命周期来控制手动开辟的内存资源。下面我们来简单实现一下着种方法:

#include <iostream>
using namespace std;
class A
{
public:
	A(pair<int, string>* ptr)
		:_ptr(ptr)
	{}
    ~A()
    {
    cout << "delete" << endl;
    }
private:
	pair<int, std:string>* _ptr;
};
double Division(int x, double y)
{
	cin >> x >> y;
	if (0 == y)
		throw invalid_argument("除数为0无法计算");
	return x / y;
}
void func()
{
	pair<int, string>* p1 = new pair<int, string>(7, "CSDN");
    A a(p1);
    int x;
    double y;
    cin >> x >> y;
	cout << Division(x, y) << endl;
	delete p1;
	p1 = nullptr;
}
int main()
{
	try
	{
		func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

1.当Division函数中不抛异常的情况,代码运行结果如下:

0928870634c94246a8fe5975d815164a.png

2.当Division函数中抛异常的情况,代码运行结果如下:

6c932c81f1c245088e28e582341fdc7c.png

 由此可见,将p1指向的资源托管给对象a控制是利于其资源释放的,p1指向的资源会随着对象a的销毁而销毁。

二、什么是RAII(智能指针的底层思想)?

智能指针是RAII技术的应用。

RAII(Resource Acquisition Is Initialization)是一种 利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效, 最后在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:
first:不需要显式地释放资源。
second:采用这种方式,对象所需的资源在其生命期内始终保持有效。
 

三、智能指针的发展历程以及模拟实现

智能指针的大体是有三个阶段的发展:第一阶段C++98的auto_ptr;第二阶段C++11的unique_ptr;第三阶段C++11中的shared_ptr。通过不断创新与努力C++11最终发布了shared_ptr,这也是智能指针的最终版本,是最优的。

我们分别来模拟实现一下这几种智能指针,以及对它们做出分析。

在此之前我们必须要明白其实智能指针是一个类对象,该类封装了所需要管理的资源,以及内部实现了具有指针功能的运算符重载成员函数(operator*  operator->)。

1.auto_ptr(C++98)

auto_ptr 的实现原理:管理权转移的思想,下面简化模拟实现了一份 keke::auto_ptr 来了解它的原
#include <iostream>
using namespace std;
namespace keke
{
	template<class T >
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}
		auto_ptr(auto_ptr& ap)
			:_ptr(ap._ptr)
		{
			//资源管理权转移
			ap._ptr = nullptr;
		}
		auto_ptr<T>& operator=(auto_ptr& ap)
		{
			//检查是否为自己给自己赋值,如果是的话那么不进行赋值
			if (_ptr != ap._ptr)
			{
				//释放被赋值对象里的资源
				if (_ptr)
					delete _ptr;
				//将对象ap的资源转移给被赋值对象
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}
		~auto_ptr()
		{
			if (_ptr)
				delete _ptr;
		}
		//要像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
}
int main()
{
	keke::auto_ptr<int> ap1(new int(5));
	keke::auto_ptr<int> ap2(ap1);
	++(*ap1);//由于ap1将资源权限转移给了ap2,则ap1_ptr==nullptr
	         //导致读取数据错误!

	++(*ap2);
	return 0;
}

b3b48b5b8afd4d61b54b385e2a675c56.png

基于上面的问题,auto_ptr是不建议被使用的。

2.unique_ptr(C++11)

unique_ptr的实现原理: 简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr 来了解它的原

unique_ptr是在auto_ptr的基础上改进的。只是将auto_ptr模拟实现中的拷贝构造函数和赋值运算符重载访问权限改为private,或者是在两个默认成员函数后加=delete。

template<class T >
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		unique_ptr(unique_ptr& up) = delete;
		unique_ptr<T>& operator=(unique_ptr& up) = delete;
		~unique_ptr()
		{
			if (_ptr)
				delete _ptr;
		}
		//要像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

智能指针unique_ptr对于不需要拷贝的场景适用,但是需要拷贝的场景则不能使用。

3.shared_ptr

C++11 中开始提供更靠谱的并且支持拷贝的 shared_ptr。
 
shared_ptr 的原理: 是通过引用计数的方式来实现多个shared_ptr对象之间共享资源
 
举一个例子:放学后最后一个走出教室的同学需要把教室的门上锁。
 
1. shared_ptr在其内部, 给每个资源都维护了着一份计数,用来记录该份资源被几个对象共
2. 在 对象被销毁时 ( 也就是析构函数调用 ),就说明自己不使用该资源了,对象的引用计数减
一。
3. 如果引用计数是 0,就说明自己是最后一个使用该资源的对象, 必须释放该资源
4. 如果不是 0,就说明除了自己还有其他对象在使用该份资源, 不能释放该资源,否则其他对
象就成野指针了。
#include <iostream>
using namespace std;
namespace keke
{
	template<class T >
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pCount(new int(1))
		{}
		shared_ptr(shared_ptr& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
		{
			++(*_pCount);
		}
		
		shared_ptr<T>& operator=(shared_ptr& sp)
		{

			if (_ptr != sp._ptr)//检查是否为自己给自己赋值
				Release();
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			++(*_pCount);
			return *this;
		}
		void Release()
		{

			if (0 == --(*_pCount) && _ptr)
			{
				cout << "delete" << endl;
				delete _ptr;
				delete _pCount;
			}
		}
		~shared_ptr()
		{
			Release();
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		int use_count()
		{
			return *_pCount;
		}
	private:
		T* _ptr;
		int* _pCount;
	};
}
int main()
{
	keke::shared_ptr<string> sp1(new string("CSDN"));
	keke::shared_ptr<string> sp2 = sp1;
	keke::shared_ptr<string> sp3(sp1);
	
	keke::shared_ptr<pair<int, string>> sp4(new pair<int, string>(5, "CSDN"));
	keke::shared_ptr<pair<int, string>> sp5(sp4);
	return 0;
}
运行结果如下:
 
39d14850788344428b9c4db955af0722.png

 shared_ptr智能指针采用计数,让最后一个释放的对象释放资源,虽然复杂一下,但是非常完美!shared_ptr也是目前主要使用的智能指针。

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Modown9.1主题无限制使用+Erphpdown17.1插件

Modown9.1主题无限制使用 1、Erphpdown17.1插件Modown9.1主题 2、送Modown主题详细教程。 1、Erphpdown插件和Modown主题无需激活 2、送的插件均无需激活 3、主题插件均不包更新 4、已亲测可以完美使用。 功能强大&#xff0c;适用于绝大多数虚拟资源站&#xff01;物超所值&a…

智慧公厕:让厕所管理变得更智慧、高效、舒适!

公共厕所是城市的重要组成部分&#xff0c;但常常被忽视。它们的管理和养护往往面临着许多问题&#xff0c;例如卫生状况不佳、环境畏畏缩缩、设施老旧等。为了解决这些问题&#xff0c;智慧公厕应运而生。智慧公厕是一种全方位的应用解决方案&#xff0c;将科技与公共厕所管理…

RERCS系统-WDA+BOPF框架实战例子 PART 2-新建Root的子节点Node Element

1、通过事务码 BOBF进入Business Object Browser&#xff08;业务对象浏览&#xff09;页面&#xff1b; 2、输入debug 进入编辑模式&#xff1b; 3、双击对应的业务对象进入Business Object Detail Browser即业务对象数据浏览器 在Node Structure的Root中新建需要的SubNode子…

【C++】学习笔记——优先级队列

文章目录 十、优先级队列1. priority_queue的介绍2. 优先级队列如何使小的数据优先级高3. 仿函数介绍4. priority_queue的模拟实现 补&#xff1a; 反向迭代器未完待续 十、优先级队列 1. priority_queue的介绍 优先级队列 其实也不属于队列&#xff0c;它跟 stack 和 queue …

[Linux_IMX6ULL驱动开发]-GPIO子系统和Pinctrl子系统

目录 Pinctrl子系统的概念 GPIO子系统的概念 定义自己的GPIO节点 GPIO子系统的函数 引脚号的确定 基于GPIO子系统的驱动程序 驱动程序 设备树修改 之前我们进行驱动开发的时候&#xff0c;对于硬件的操作是依赖于ioremap对寄存器的物理地址进行映射&#xff0c;以此来达…

SpringBoot实现统一返回值+全局异常处理

在这里首先感谢的就是程序员老罗&#xff0c;从他的项目里面学到了这些东西。 首先就是去创建一个SpringBoot项目&#xff0c;这里我就不多做赘述了 封装一个统一返回对象 package com.example.demo.vo;public class ResponseVO<T> {private String status;private In…

数据驱动实战二

目标 掌握数据驱动的开发流程掌握如何读取JSON数据文件巩固PO模式 1. 案例 对TPshop网站的登录模块进行单元测试 1.1 实现步骤 编写测试用例采用PO模式的分层思想对页面进行封装编写测试脚本定义数据文件&#xff0c;实现参数化 1.2 用例设计 1.3 数据文件 {"login…

JVM调优—减少FullGC

背景 最近负责了一个审批流程新项目&#xff0c;带领了几个小伙伴&#xff0c;哼哧哼哧的干了3个月左右&#xff0c;终于在三月底完美上线了&#xff0c;好消息是线上客户用的很丝滑&#xff0c;除了几个非常规的业务提单之外&#xff0c;几乎没有什么大的问题&#xff0c;但是…

4D 成像毫米波雷达:新型传感器助力自动驾驶

1 感知是自动驾驶的首要环节&#xff0c;高性能传感器必不可少 感知环节负责对侦测、识别、跟踪目标&#xff0c;是自动驾驶实现的第一步。自动驾驶的实现&#xff0c;首先要能够准确理解驾驶环境信息&#xff0c;需要对交通主体、交通信号、环境物体等信息进行有效捕捉&#x…

【静态分析】软件分析课程实验A4-类层次结构分析与过程间常量传播

官网&#xff1a;作业 4&#xff1a;类层次结构分析与过程间常量传播 | Tai-e 参考&#xff1a;https://www.cnblogs.com/gonghr/p/17984124 ----------------------------------------------------------------------- 1 作业导览 为 Java 实现一个类层次结构分析&#xf…

249 基于matlab的MED、OMEDA、MOMEDA、MCKD信号处理方法

基于matlab的MED、OMEDA、MOMEDA、MCKD信号处理方法。最小熵反褶积(MED)&#xff0c;最优最小熵反卷积调整卷积 (OMEDA),多点最优最小熵解卷积调整&#xff08;Multipoint Optimal Minimum Entropy Deconvolution Adjusted&#xff0c;MOMEDA&#xff09;&#xff0c;最大相关峭…

【智能优化算法】蜜獾优化算法(Honey Badger Algorithm,HBA)

蜜獾优化算法(Honey Badger Algorithm,HBA)是期刊“MATHEMATICS AND COMPUTERS IN SIMULATION”&#xff08;IF 3.6&#xff09;的2022年智能优化算法 01.引言 蜜獾优化算法(Honey Badger Algorithm,HBA)受蜜獾智能觅食行为的启发&#xff0c;从数学上发展出一种求解优化问题的…

vue3实现动态表格

vue3结合element-plus实现动态表格&#xff0c;可添加、删除、对单行数据判断。 实现效果&#xff1a;查看源代码 实现代码&#xff1a; <div class"arrTable-Box"><el-table :data"tableData" border max-height"250"><el-t…

【强训笔记】day17

NO.1 思路&#xff1a;用一个字符串实现&#xff0c;stoi函数可以转化为数字并且去除前导0。 代码实现&#xff1a; #include <iostream> #include<string> using namespace std;string s;int main() {cin>>s;for(int i0;i<s.size();i){if(s[i]%20) s[…

大模型模型简化机器人训练;简单易用的 3D 工具Project Neo;特斯拉放出了擎天柱机器人最新训练视频

✨ 1: DrEureka 利用大语言模型自动化将机器人仿真环境训练结果转移到真实世界 DrEureka是一种利用大型语言模型&#xff08;LLMs&#xff09;自动化和加速从仿真&#xff08;sim&#xff09;到现实世界&#xff08;real&#xff09;转移的技术。在机器人技能学习领域&#x…

网络编程套接字和传输层tcp,udp协议

认识端口号 我们知道在网络数据传输的时候&#xff0c;在IP数据包头部有两个IP地址&#xff0c;分别叫做源IP地址和目的IP地址。IP地址是帮助我们在网络中确定最终发送的主机&#xff0c;但是实际上数据应该发送到主机上指定的进程上的&#xff0c;所以我们不仅要确定主机&…

MultiBoot 和 QuickBoot

目录 MultiBoot简介MultiBoot 实现方式设置 bitstream 属性使用 ICAPE2 原语WBSTAR 寄存器定义 MultiBoot 工作流程生成mcs固化文件 Tcl 指令Fallback状态寄存器MultiBoot 正常加载状态看门狗1超时状态看门狗2超时状态CRC 错误和无 DESYNC 命令IDCODE 错误状态CRC 错误状态 Wat…

【AMBA Bus ACE 总线 8 -- ICache maintenance】

请阅读【AMBA Bus ACE 总线与Cache 专栏 】 欢迎学习:【嵌入式开发学习必备专栏】 文章目录 ACE ICache maintenanceACE ICache maintenance 图 1-1 当一个OS run 多个cpu的时候,根据调度算法的不同,OS 可以根据调度算法的不同分别 run 在某个具体的CPU上,因此,它们会有…

Linux修炼之路之初识操作系统+基础指令(1)

目录 引言 一&#xff1a;对操作系统(OS)的简单了解 1.操作系统(OS) 是什么 2.操作系统好坏的衡量标准 3.操作系统存在的重要性 4.理解所有在计算机上的操作 二&#xff1a;Linux与windows操作的特点区别 三&#xff1a;基础指令 1.ls 指令 1.使用 2.常用选项 2.…

【C++语言】Date类的代码实现(操作符重载运用)

文章目录 前言Date类的构思Date类的相关实现基本框架&#xff08;默认成员函数&#xff09;计算n天前\后的日期补充&#xff1a;前置、后置说明判断两个日期的关系&#xff08;大于&#xff0c;小于等&#xff09;&#xff1b;可以计算两个日期之间相差多少天补充&#xff1a;流…