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

 

目录

 

前言

一、智能指针有什么用?

二、什么是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.mzph.cn/pingmian/9736.shtml

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

相关文章

【vulhub靶场】Apache 中间件漏洞复现

【vulhub靶场】Apache 中间件漏洞复现 一、Apache HTTPD 换行解析漏洞&#xff08;CVE-2017-15715&#xff09;1. 漏洞详情2. 影响版本3. 漏洞复现 二、Apache多后缀解析漏洞&#xff08;apache_parsing_vulnerability&#xff09;1. 漏洞详情2. 漏洞复现 三、Apache HTTP Serv…

【Python爬虫实战入门】:教你一个程序实现PPT模版自由

文章目录 &#x1f4a5;一、PPT模版爬取&#x1f525;1.1 第一个爬虫&#x1f6b2;1. 获取下载页面链接 ❤️1.2 第二个爬虫&#x1f6b2;1.3 第三个爬虫&#x1f388;2. 文件保存 ❤️1.4 翻页处理 &#x1f525;二、完整代码 &#x1f525;&#x1f525;&#x1f525; Pytho…

【Linux】简易进度条的实现

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解Linux中进度条的实现的相关内容。 如果看到最后您觉得这篇文章写得…

深度学习论文: LightGlue: Local Feature Matching at Light Speed

深度学习论文: LightGlue: Local Feature Matching at Light Speed LightGlue: Local Feature Matching at Light Speed PDF: https://arxiv.org/pdf/2306.13643 PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTorch代码: https://github.com/shanglianlm0525/…

AI算法-高数2-导数定义和公式

P14 2.1 导数的定义(一):2.1 导数的定义_哔哩哔哩_bilibili 导数定义&#xff1a; 导数公式&#xff1a; P15 2.1 导数的定义(二)&#xff1a;2.1 导数的定义&#xff08;二&#xff09;_哔哩哔哩_bilibili [a,b]可导&#xff0c;a的端点&#xff1a;右可导&#xff0c;b端点&…

Leetcode—138. 随机链表的复制【中等】(cend函数)

2024每日刷题&#xff08;129&#xff09; Leetcode—138. 随机链表的复制 实现代码 /* // Definition for a Node. class Node { public:int val;Node* next;Node* random;Node(int _val) {val _val;next NULL;random NULL;} }; */class Solution { public:Node* copyRan…

Spring JdbcTemplate使用临时表+事务会话管理实现数据新增、查询及自动清除功能

需求描述&#xff1a; 由于某些情况下当查询过滤参数过大时&#xff0c;执行sql由于参数过大而报错&#xff0c;此时 需要使用临时表的方式&#xff0c;即 当参数超过某个阀值&#xff08;如 1000&#xff0c;可调整&#xff09;新增一张临时表&#xff0c;将原表 与 该临时表进…

代码随想录算法训练营第六十二天|503.下一个更大元素II、42.接雨水

代码随想录算法训练营第六十二天|503.下一个更大元素II、42.接雨水 503.下一个更大元素II 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元…

第十一篇:操作系统新纪元:智能融合、量子跃迁与虚拟现实的交响曲

操作系统新纪元&#xff1a;智能融合、量子跃迁与虚拟现实的交响曲 1 引言 在数字化的浪潮中&#xff0c;操作系统如同一位智慧的舵手&#xff0c;引领着信息技术的航船穿越波涛汹涌的海洋。随着人工智能、物联网、量子计算等前沿技术的蓬勃发展&#xff0c;操作系统正站在一个…

富士Apeos 2350 NDA复印机报062 360代码故障

故障描述&#xff1a; 富士Apeos 2350 NDA复印机新机器刚拆箱安装&#xff0c;开机正常&#xff0c;自检扫描头一卡一卡的往前动几下就不动了、扫描灯也不亮扫描头也不能正常复位&#xff1b;按机器的复印键直接报062 360代码&#xff1b; 解答&#xff1a; 此代码为扫描故障&a…

PDF高效编辑:一键批量,PDF转图片的快速解决方案

在数字化时代&#xff0c;PDF文件已成为工作和学习中不可或缺的一部分。然而&#xff0c;有时我们可能需要将PDF转换为图片&#xff0c;以便更轻松地编辑、共享或处理。为了满足这一需求&#xff0c;许多高效的PDF编辑工具应运而生&#xff0c;其中“办公提效工具”一键批量PDF…

【每日八股】淘天一面

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害 rocketmq的消息重复发送问题&#xff1f;如何保证幂等&#xff1f; 如何保证幂等性&#xff1a; 消息 Key 设置&#xff1a;不建议…

如何自定义Markdown中插入图片的位置

工作中常常需要在VsCode下写Markdown笔记&#xff0c;在写笔记的过程中不免需要插入图片。  Markdown中插入笔记的操作往往是比较繁琐的&#xff0c;比如&#xff1a;在文档中引用本地某个文件夹下的图片&#xff0c;首先需要你先保存图片到本地路径&#xff0c;然后需要你在文…

Vue-Cli脚手架项目的搭建【新手快速入手】

目录 一、Vue CLI脚手架简介☺ 1.Node.js前置环境的安装 2.安装npm管理器 3.安装淘宝镜像(cnpm) 二、安装vue-cli 1. 版本号查看 2.旧版本卸载 3.新版本安装 4.检查 三、Vue项目的搭建 &#x1f4cc;进入Vue项目管理器 ★命令方式创建 若localhost拒绝访问怎么办&…

技术速递|Python in Visual Studio Code 2024年5月发布

排版&#xff1a;Alan Wang 我们很高兴地宣布 Visual Studio Code 的 Python 和 Jupyter 扩展将于 2024 年 5 月发布&#xff01; 此版本包括以下公告&#xff1a; “Implement all inherited abstract classes”代码操作新的自动缩进设置Debugpy 从 Python 扩展中删除&#…

Unity TileMap入门

概述 相信很多同学学习制作游戏都是从2D游戏开始制作的吧&#xff0c;瓦片地图相信大家都有接触&#xff0c;那接下来让我们学习一下这部分的内容吧&#xff01; Tilemap AnimationFrameRate:设置每帧动画的播放速率。Color:瓦片地图的颜色TileAnchor:锚点&#xff0c;&#x…

Docker容器创建各种镜像

目录 1.Docker创建Mysql容器 2.Docker创建Redis容器 3.Docker 创建minio容器 1.Docker创建Mysql容器 拉取镜像 docker pull mysql:8.0.20 宿主机创建挂载目录 /opt/mysql/conf /opt/mysql/data /opt/mysql/log 启动命令 docker run --name mysql -p 3306:3306 -v /opt/…

Set接口

Set接口的介绍 Set接口基本介绍 无序&#xff08;添加和取出的顺序不一致&#xff09;&#xff0c;没有索引不允许重复元素&#xff0c;所以最多包含一个nullJDK API中Set接口的实现类&#xff1a;主要有HashSet&#xff1b;TreeSet Set接口的常用方法 和List 接口一样&am…

Windows11提示升级,我要点确定吗?

Windows 11自2021年10月推出以来&#xff0c;一直保持着缓慢而稳定的增长&#xff0c;相对来说在游戏PC上的影响力较大&#xff0c; 而Windows 10也即将于2025年10月结束支持。 截至2024年&#xff0c;Windows 11的主要版本包括24H2、 23H2、22H2和21H2。 24H2&#xff1a;20…

k8s遇到的常见问题及解决

1. error: open /var/lib/kubelet/config.yaml: no such file or directory 解决&#xff1a;关键文件缺失&#xff0c;多发生于没有做 kubeadm init就运行了systemctl start kubelet。 要先成功运行kubeadm init 2. 执行初始化kubeadm init ------的时候报错 The HTTP call…