C++的智能指针

news2024/10/2 14:35:57

在这里插入图片描述

文章目录

  • 1. 内存泄漏
    • 1.1 什么是内存泄漏
    • 1.2 内存泄漏分类
  • 2. 为什么需要智能指针
  • 3. 智能指针的使用及原理
    • 3.1 RAII
    • 3.2 使用RAII思想设计的SmartPtr类
    • 3.3 让SmartPtr像指针一样
    • 3.3 SmartPtr的拷贝
    • 3.4 auto_ptr
    • 3.5 unique_ptr
    • 3.6 shared_ptr
      • 3.6.1 shared_ptr的循环引用
      • 3.6.2 weak_ptr
    • 3.7 定制删除器

1. 内存泄漏

1.1 什么是内存泄漏

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

内存泄漏的危害长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

1.2 内存泄漏分类

C/C++程序中一般我们关心两种方面的内存泄漏:
堆内存泄漏(Heap leak):
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

系统资源泄漏:
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

2. 为什么需要智能指针

我们可以采用RAII思想或者智能指针来管理资源,来避免内存泄漏。

举个例子:
在这里插入图片描述
这里有两个new出来的数组,如果new出现了异常,那么就会直接被main函数catch到。如果是第一个出现异常,可以直接被捕捉去。如果是第二个出现异常,被main里面的catch捕捉,那么第一个new就没有办法释放。久而久之,可能就会造成内存泄漏。如果我们把第二个new放到try里面,如果第二个new出现异常,被catch到,但是第二个new失败了,怎么能delete呢?所以这些都不是一个好方法,所以我们需要用到智能指针。

3. 智能指针的使用及原理

3.1 RAII

RAII是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象

总而言之,获取到资源以后去初始化一个对象,将资料交给对象管理

这种做法有两大好处:
1.不需要显式地释放资源。
2.采用这种方式,对象所需的资源在其生命期内始终保持有效

3.2 使用RAII思想设计的SmartPtr类

在这里插入图片描述
我们这里把构造函数和析构函数先写出来。我们再去申请资源的时候就用这个类来管理:
在这里插入图片描述
在这里插入图片描述
在析构函数这里加上一个打印函数。方便我们观察。
在这里插入图片描述
不抛异常,可以正常释放。
在这里插入图片描述
抛异常也可以正常释放。如果是第一个new出现错误,直接被main里面的catch捕捉,没有事情。如果是第二个new出现错误,也会直接被捕捉,当函数栈帧销毁的时候,这个类会自动调用它的析构函数完成销毁。div()也是一样的道理,如果里面抛了除0错误,那么前面两个就会自动调用析构函数销毁。

3.3 让SmartPtr像指针一样

虽然上面管理了资源,但是我们想使用的时候就不好办了。我们需要让这个类像指针一样去使用:
在这里插入图片描述
当我们把解引用和箭头都写上时,我们就可以把这个类当指针一样去使用了。
在这里插入图片描述

3.3 SmartPtr的拷贝

举个例子:
在这里插入图片描述
因为这里是内置类型,会形成值拷贝,那么析构的时候就会析构两次,发生错误。
在这里插入图片描述

那么这里能不能使用深拷贝呢
答案是:不能的。因为智能指针的目的就是托管我们这个地值的资源,如果你深拷贝了,就不是这部分资源了,所以这里必须是浅拷贝

下面我们就说一说怎么解决这种问题。

3.4 auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针。
在这里插入图片描述
文档链接 auto_ptr的实现原理:管理权转移的思想
在这里插入图片描述
这里sp1被构造出来初始化了,我们再进行下一步。
在这里插入图片描述
拷贝构造后,sp1就不能使用了。

auto_ptr拷贝构造的代码实现:
在这里插入图片描述
这些解决方法除了拷贝构造的原理不一样,其它的都和SmartPtr类一样。但是这个auto_ptr在日常中不建议使用。

3.5 unique_ptr

在C++11标准库中,出现了一些比较好的解决方法,第一个就是unique_ptr,它包含在< memory >头文件中。
在这里插入图片描述
文档链接 unique_ptr的实现原理:不让拷贝

在这里插入图片描述
它就是直接不让你拷贝了。

unique_ptr拷贝构造的代码实现:

方法一:只声明,不实现,并且声明设置成私有
在这里插入图片描述
如果我们不声明,那么编译器会默认生成一个。如果不设置成私有,那么可能会模板特化自己定义。

方法二:在C++11中,声明后加个delete
在这里插入图片描述
但是还没有防死,赋值问题没有解决:
在这里插入图片描述
我们还需要把赋值重载给写上:
在这里插入图片描述
在这里插入图片描述

3.6 shared_ptr

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr。
在这里插入图片描述
文档链接 shared_ptr的实现原理:引用计数,记录几个对象管理这块资源,析构的时候- -计数,最后一个析构的对象释放资源

在这里插入图片描述
这里就没有任何问题了。

shared_ptr拷贝构造的代码实现:
那么可能有的人会想用static做一个成员变量来计数:
在这里插入图片描述
但是这样是不可以的。
在这里插入图片描述
前面两个智能指针应该里面的计数应该是2,第三个智能指针里面的计数是1。但是如果是static,它是所有这个类的对象共用一个,所以会出现问题。

正确的解决办法:
在这里插入图片描述
我们用指针来指向动态开辟的空间,当有新的智能指针时,我们就new一个。
在这里插入图片描述
拷贝构造的时候,我们将需要拷贝的对象的pCount拷贝过来++一下。
在这里插入图片描述
析构的时候,当里面为最后一个时候,把空间都给释放掉。
在这里插入图片描述
我们可以演示一下:
在这里插入图片描述
可以看到前面两个的pCount是2,第三个是1,这些都是没有问题的。

但是我们还需要处理赋值情况:
在这里插入图片描述
赋值情况就会出现问题,该加加没有加加,该减减的没有减减。

代码实现:
在这里插入图片描述
首先,自己就不需要给自己赋值了,那么我们可以这样去写:
在这里插入图片描述
不过这样写还是存在一些问题:

	sp1 = sp1;//可以避免
	sp1 = sp2;//不可以避免

因为sp2虽然不是和sp1同一个,但是它们的指向空间是同一个,所以第二个防不住,但是不会造成问题,只是没有作用。我们可以直接比较_ptr:
在这里插入图片描述
然后我们需要将this的对象进行减减,sp的对象进行加加。
在这里插入图片描述
这样还是不行:

	sp1 = sp3;
	sp2 = sp3;

如果是这种情况,那么原来sp1和sp2指向的空间没有指针去管理了,会造成内存泄漏。
在这里插入图片描述
所以我们需要把析构这个部分给加上,如果原来的pCount为0,就把这段空间销毁。

3.6.1 shared_ptr的循环引用

举个例子:
在这里插入图片描述
这是我们平时的一种简单用法,如果我们改成智能指针的话:
在这里插入图片描述
但是这里出现了问题:node1->_next和node1->_prev是原生指针,而node1和node2是一个指针指针对象,不能相互赋值。
我们需要将上面的也改成智能指针:
在这里插入图片描述
虽然编译可以通过,但是不能析构了。
在这里插入图片描述

循环引用分析
1.node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete
在这里插入图片描述
2. node1的_next指向node2,node2的_prev指向node1,引用计数就会变成2
在这里插入图片描述
3. node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点
在这里插入图片描述
4. 此时的情况是:_next析构了,右边结点才能释放,_prev析构了,左边结点才能释放。所以这就叫循环引用,谁也不会释放

解决方法如下。

3.6.2 weak_ptr

在这里插入图片描述
文档链接 原理就是:node1->_next = node2和node2->_prev = node1时weak_ptr的_next和_prev不会增加node1和node2的引用计数

在这里插入图片描述
在这里插入图片描述
weak_ptr其实就是为了辅助shared_ptr,它不参与资源管理。

模拟实现代码:
因为weak_ptr不参与资源管理,所以它不是RAII,我们不需要pCount和析构函数:
在这里插入图片描述
最重要的是能够拷贝构造shared_ptr。那么赋值也是一样的道理。
在这里插入图片描述
我们只需要指向这段空间,不需要加计数了。

3.7 定制删除器

前面模拟实现的都是new出来的,如果是malloc或者是new []这样的方式,可能就会报错,这里设计了一个删除器来解决这个问题。

举个例子:
在这里插入图片描述
在这里插入图片描述
这里报错的原因是:delete和new []不匹配。平时我们用的unique_ptr/shared_ptr 默认释放资源用的delete

如何匹配申请方式去对应释放呢
答案是:用仿函数来做。

在这里插入图片描述
运行结果如下:
在这里插入图片描述
其它开辟空间的方式也可以用这样的方法:
在这里插入图片描述

shared_ptr虽然和unique_ptr原理类似,但是也有一点区别:
在这里插入图片描述
unique_ptr是类模板参数传参,而shared_ptr是构造函数时传参,一个传类型,一个传对象。传对象时,我们用lambda表达式比较方便。

完善unique_ptr和shared_ptr:
在这里插入图片描述

但是shared_ptr不能改造,因为标准库里实现的太复杂了。

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

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

相关文章

MYSQL-数据库管理(上)

一、数据库概述 一、数据库基本概念 1.1 数据 1&#xff09; 描述事物的符号记录称为数据&#xff08;Data&#xff09;。数字、文字、图形、图像、声音、档案记录等 都是数据。 2&#xff09;数据是以“记录”的形式按照统一的格式进行存储的&#xff0c;而不是杂乱无章的。…

机器学习之分类决策树与回归决策树—基于python实现

大家好&#xff0c;我是带我去滑雪&#xff01; 本期为大家介绍决策树算法&#xff0c;它一种基学习器&#xff0c;广泛应用于集成学习&#xff0c;用于大幅度提高模型的预测准确率。决策树在分区域时&#xff0c;会考虑特征向量对响应变量的影响&#xff0c;且每次仅使用一个分…

vs编译生成动态库

说明 windows版本&#xff0c;vs2019 创建一个动态库 新建一c项目&#xff0c;创建一个dll类型项目。 在头文件中添加一个mylib.h文件&#xff1a; #pragma once#ifndef MYLIB_H #define MYLIB_Hextern "C" __declspec(dllexport) void Hello(); extern "C…

UG NX二次开发(C++)-建模-修改NXObject或者Feature的颜色(一)

文章目录 1、前言2、在UG NX中修改Feature的颜色操作3、采用NXOpen(C)实现3.1 创建修改特征的方法3.2 调用ModifyFeatureColor方法3.3 测试结果 1、前言 在UG NX中&#xff0c;改变NXObject和Feature的操作是不相同的&#xff0c;所以其二次开发的代码也不一样&#xff0c;我们…

优化问题的拉格朗日Lagrange对偶法原理

首先我们定义一般形式的求解x的优化问题&#xff1a; 表示优化的目标函数&#xff0c;上述为最小优化&#xff0c;实际上最大优化可以改写为的形式表示第i个不等式约束表示等式约束 1. Lagrange对偶问题 上述优化问题的拉格朗日Lagrange对偶法求解&#xff0c;是将上述带约束…

Baumer工业相机堡盟工业相机如何联合BGAPISDK和Halcon实现图像的直方图算法增强(C#)

Baumer工业相机堡盟工业相机如何联合BGAPISDK和Halcon实现图像的直方图算法增强&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机使用图像算法增加图像的技术背景Baumer工业相机通过BGAPI SDK联合Halcon使用直方图图像增强算法1.引用合适的类文件2.BGAPI SDK在图像回调…

车牌识别系统完整商用级别设计流程

简介 车牌识别&#xff08;License Plate Recognition&#xff09;是一种通过计算机视觉技术识别和提取车辆车牌上字符信息的技术。它在交通管理、智慧停车、安防监控等领域有着广泛的应用。 本项目将带完整的了解车牌识别系统设计思路&#xff0c;以及实现流程。 算法部分应…

图书管理系统【控制台+MySQL】(Java课设)

系统类型 控制台类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87737294 更多系统资源库地…

机器学习 --- 绪论

第1关&#xff1a;什么是机器学习 任务描述 ​ 本关任务&#xff1a;根据本节课所学知识完成本关所设置的选择题。 相关知识 ​ 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 什么是机器学习。 什么是机器学习 ​ 相信大家一定都非常喜欢吃西…

【Python】【进阶篇】21、Django Admin数据表可视化

目录 21、Django Admin数据表可视化1. 创建超级用户2. 将Model注册到管理后台1)在admin.py文件中声明 3. django_admin_log数据表 21、Django Admin数据表可视化 在《Django Admin后台管理系统》介绍过 Django 的后台管理系统是为了方便站点管理人员对数据表进行操作。Django …

【Java面试八股文】数据库篇

导航&#xff1a; 【黑马Java笔记踩坑汇总】JavaSEJavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线MySQL高级篇设计模式牛客面试题 目录 请你说说MySQL索引,以及它们的好处和坏处 请你说说MySQL的索引是什么结构,为什么不用哈希表 请你说说数据库索引的底…

R语言 | 编写自己的函数

目录 一、正式编写程序 二、设计第一个函数 三、函数也是一个对象 四、程序代码的简化 五、return()函数的功能 六、省略函数的大括号 七、传递多个参数函数的应用 7.1 设计可传递2个参数的函数 7.2 函数参数的默认值 7.3 3点参数“…”的使用 八、函数也可以作为参数 …

《针灸》笔记(倪海厦先生人纪系列针灸篇)

程序员上了年纪&#xff0c;各种职业病就来了&#xff0c;人工智能成为好工具的同时&#xff0c;自己的时间也多了一些。 了解他才能判断他的真伪&#xff0c;没学过就认为中医是糟粕的&#xff0c;请划走。 学到什么记什么&#xff0c;线上线下齐下手&#xff0c;自用笔记&…

【Python基础入门学习】Python高级变量你了解多少?

认识高级变量 1. 列表 list1.1 列表的定义1.2 列表常用操作关键字、函数和方法 1.3 循环遍历1.4 列表嵌套1.5 应用场景 2. 元组 tuple2.1 元组的定义2.2 元组常用操作2.3 应用场景 3. 字典 dictionary3.1 字典的含义3.2 字典常用操作3.3 应用场景 4. 字符串 string4.1 字符串的…

基于PyQt5的图形化界面开发——模拟医院管理系统

基于PyQt5的图形化界面开发——模拟医院管理系统 0. 前言1. 需求分析2. 挂号界面的思路、UI界面代码及相应触发函数2.1 思路分析2.2 ui_guahao.py2.3 相应的触发函数代码 3. 就诊界面的思路、UI界面代码及相应触发函数3.1 思路分析3.2 ui_jiuzhen.py3.3 相关触发函数&#xff1…

如何制定专属于自己的个人目标

文章目录 前言一、自问自答二、制定目标的原则1.明确性2.衡量性3.可实现性4.相关性5.时限性 三、对目标进行计划1.5W2H1.做什么&#xff08;what to do it&#xff09;2.为什么做&#xff08;why to do it)3.何时做&#xff08;when to do it&#xff09;4.何地做&#xff08;w…

8b/10b编码方式(详细)总结附实例快速理解

目录 前言一、简介二、转换过程2.1 背景2.2 具体转换过程 三、其他相关链接1、PCI总线及发展历程总结2、PCIe物理层总结-PCIE专题知识&#xff08;一&#xff09;3、PCIe数据链路层图文总结-PCIe专题知识&#xff08;二&#xff09; 前言 本文主要通过图文方式介绍8b/10b编码&…

Hystrix线程池问题

背景&#xff1a;在一个以springcloud为基础架构的微服务项目中&#xff0c;活动期间并发量一大就会出现服务调用失败的问题。经定位发现&#xff0c;被调用服务中无对应的请求日志&#xff0c;继续通过日志查询确认是feign调用时出现服务降级&#xff0c;进入降级方法统一返回…

教会你制作自己的浏览器 —— 并将 ChatGPT 接入

前期回顾 分享24个强大的HTML属性 —— 建议每位前端工程师都应该掌握_0.活在风浪里的博客-CSDN博客2分享4个HTML5 属性&#xff0c;开发必备https://blog.csdn.net/m0_57904695/article/details/130465836?spm1001.2014.3001.5501 &#x1f44d; 本文专栏&#xff1a;开发…

对抗性攻击和防御竞争(Adversarial Attacks and Defences Competition)的通俗解读

通过我们在Pytorch对机器学习模型的安全漏洞攻击方法之Fast Gradient Sign Attack(FGSM,快速梯度符号攻击)https://blog.csdn.net/weixin_41896770/article/details/130319089 这篇文章的介绍&#xff0c;我们了解到FGSM可以对机器学习模型进行有效的攻击&#xff0c;成功的让M…