C++的智能指针 RAII

目录

产生原因

RAII思想

C++11的智能指针

智能指针的拷贝与赋值 

 shared_ptr的拷贝构造

shared_ptr的赋值重置

shared_ptr的其它成员函数

weak_ptr

定制删除器

简单实现


产生原因

产生原因:抛异常等原因导致的内存泄漏

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{// 1、如果p1这里new 抛异常会如何?// 2、如果p2这里new 抛异常会如何?// 3、如果div调用这里又会抛异常会如何?int* p1 = new int;int* p2 = new int;cout << div() << endl;delete p1;delete p2;
}int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

RAII思想

基本概念:智能指针(Smart Pointer)是C++中一种用于自动管理动态分配内存的对象,旨在防止内存泄漏和指针悬挂问题

核心理念:RAII,即一种利用对象生命周期来控制程序资源(如内存、网络连接等)的技术

工作原理:使用一份资源时,先用该资源构造一个智能指针,智能指针会保证在指向资源仍存在时始终有效(该资源的释放在智能指针的析构函数中),智能指针是由一个匿名对象构建得,为了能像指针一样使用,智能指针类中还会重载*和-> 

#include<iostream>
using namespace std;//智能指针类
template<class T>
class SmartPtr
{
public:// RAIISmartPtr(T* ptr)//接收一份资源的地址:_ptr(ptr)//_ptr指向一份资源{}~SmartPtr(){cout << "delete:" << _ptr << endl;delete _ptr;//智能指针对象释放时才释放_ptr指向的资源}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{//创建一个int* 类型的智能指针SmartPtr<int> sp1(new int);SmartPtr<int> sp2(new int);*sp1 += 10;//像指针一样可以*SmartPtr<pair<string, int>> sp3(new pair<string, int>);//智能指针sp3指向得是一个pair类型的匿名对象sp3->first = "apple";sp3->second = 1;//等价于sp3.operator->()->second = 1;cout << div() << endl;
}int main()
{try{Func();}catch (const exception& e){cout << e.what() << endl;}return 0;
}

C++11的智能指针

基本概念:C++98时就已经提供了一个叫auto_ptr的智能指针,但使用该智能指针进行拷贝时,会出现管理权的转移,原对象的资源管理权交给了拷贝得到的新对象,这导致指向原对象的指针变为空指针,再次使用可能会报错;所以C++11在boost库的基础上引入了unique_ptr 和 shared_ptr等新的智能指针类C++11的智能指针均包含在<memory>头文件中,需要显示引用)

智能指针的拷贝与赋值 

1、unique_ptr 类不支持拷贝构造和赋值重载(通过只声明不定义实现,禁止了拷贝构造最好将赋值重载也禁掉,不禁掉则赋值重载是默认提供的会进行浅拷贝),适用于不需要拷贝的场景

2、shared_ptr 类有拷贝构造和赋值重载,并使用引用计数解决释放的问题(不使用静态成员进行计数是因为静态成员记录的是所有与被管理资源同类型的资源被使用的次数,即同类型的两个不同资源new出来的两个智能指针不会使得计数器++,而是重置变为1,当然我们设计得拷贝构造仍会使计数器++,但是new时可能会导计数器重置)

 shared_ptr的拷贝构造

基本概念:智能指针shared_ptr在创建时,除了有指向资源的指针ptr,还有该资源独属得计数器指针int * _count,初始时*(_count) = 1

// 构造函数
shared_ptr(T* ptr = nullptr):_ptr(ptr),_pcount(new int(1)){}// 拷贝构造: sp2(sp1)
shared_ptr(const shared_ptr<T>& sp)
{_ptr = sp._ptr;_pcount = sp._pcount;// 拷贝时++计数++(*_pcount);
}

shared_ptr的赋值重置

基本概念:shared_ptr的赋值重载需要考虑无效赋值的情况

//赋值重置, sp1 = sp4
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{//if (this != &sp),防止自我赋值的情况,基础版if (_ptr != sp._ptr)//避免管理统一资源的两个智能指针对象间的互相赋值,升级版{release();//先进行释放函数,防止内存泄漏_ptr = sp._ptr;_pcount = sp._pcount;// 拷贝时++计数++(*_pcount);}return *this;
}//释放函数
void release()
{// 先进行计数器--,如果--后智能指针管理的资源的计数器变为0,就释放指向该资源的指针和计数器指针,然后再进行其它操作if (--(*_pcount) == 0){cout << "delete:" << _ptr << endl;delete _ptr;delete _pcount;}
}

shared_ptr的其它成员函数

基本概念:shared_ptr的析构函数并会直接将其管理的资源直接释放,还是要调用一个release函数进行--_count判断,即使是shared_ptr析构后,如果之前存在对该资源管理权的拷贝或者赋值,则用于记录该资源的计数器指针仍然不会被销毁,因为在拷贝或赋值时又有一个新的智能指针备份了该资源的信息(计数器指针指向的对象是new出来的int类型的对象除非主动释放,否则不会消失)

//析构函数
~shared_ptr()
{// 析构时,--计数,计数减到0,release();
}int use_count()
{return *_pcount;
}// 像指针一样
T& operator*()
{return *_ptr;
}T* operator->()
{return _ptr;
}T* get() const//为别人提供自己的指针,且该函数中不能修改自己的指针
{return _ptr;
}

shared_ptr的缺陷

基本概念:两个或多个对象通过 shared_ptr 相互引用,从而形成了一个循环。此时,即使所有外部 shared_ptr 都被销毁,由于循环引用中的 shared_ptr 仍然存在,引用计数永远不会降为零,导致这些对象无法被释放,造成内存泄漏

1、防止循环链表两个的结点因抛异常导致的无法释放,我们使用share_ptr管理这两个结点:

struct ListNode
{int _val;//④使得下面的n1->next = n2之类的操作不会因为双方类型不同导致无法互相赋值//struct ListNode* _next;//struct ListNode* _prev;//|//vstd::shared_ptr<ListNode> _next;std::shared_ptr<ListNode> _prev;ListNode(int val = 0):_val(val){}
};int main()
{//①ListNode* n1 = new ListNode(10);//ListNode* n2 = new ListNode(20);//|//vstd::shared_ptr<ListNode> n1 = ((new) ListNode(10);std::shared_ptr<ListNode> n2 = ((new) ListNode(20);//②中间可能出现抛异常    //|//v//不用担心抛异常了n1->next = n2;n2->prev = n1;//④delete n1;//delete n2;//此时不需要在这里delete了,在智能指针内部会deletereturn 0;
}

2、但是此时又会出现下面三种情况:

        两个结点互相用自己的智能指针管理对方时,管理两个结点的原始智能指针都析构后,记录两个结点的计数器均为1不会变为0,即两个结点均不会释放,此时出现内存泄漏问题,此时如果想要先释放右结点,那么就会出现以下的循环:

  • 这就是shared_ptr在特殊场景下的缺陷......

weak_ptr

 文档:weak_ptr - C++ Reference (cplusplus.com)

基本概念:为了解决shared_ptr在特殊场景下的缺陷,C++11还引入了weak_ptr,该智能指针不增加引用计数,不管理对象的生命周期

 注意事项:

1、weak_ptr不支持RAII(下面是便于理解的简化实现)

// 不支持RAII,不参与资源管理
template<class T>
class weak_ptr
{public:weak_ptr():_ptr(nullptr)//将传入的指针直接置空即可,不参与资源的管理{}weak_ptr(const shared_ptr<T>& sp){_ptr = sp.get();//获取shared_ptr的指针}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}// 像指针一样T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};

2、weak_ptr也有use_count函数,用于检测此时与weak_ptr指向同一资源的shared_ptr的个数,如果shared_ptr为0(该资源已经被释放了)则weak_ptr变为野指针,应及时置空

3、weak_ptr的expired函数用于检查weak_ptr是否过期,过期返回真,未过期返回假,相比use_count会更方便

总结:使用智能指针不一定会避免内存泄漏(上述结点时使用的纯shared_ptr),正确的使用智能指针才能避免内存泄漏(完善后的shared_ptr + weak_ptr)

定制删除器

基本概念: C++11并没有像boost库中的那样提供shared_array等用于管理和释放由[ ]创建的多个对象的智能指针,所以在使用shared_ptr管理和释放由[ ]开辟的多个对象时,会因为delete与new的方式不匹配而报错(shared_ptr的new和delete均为(),但如是[],释放时仍为()就可能导致出错)

std::shared_ptr<ListNode> p1(new ListNode(10));//正常情况
std::shared_ptr<ListNode> p1(new ListNode[10]);//有[]的情况,无法正确释放

因此C++11的智能指针还提供了接受自定义删除器的构造方式:

template <class U, class D> shared_ptr (U* p, D del);
  • D del自定义删除器,可以是函数指针、函数对象、Lambda 表达式等

1、自定义删除器是函数对象

template<class T>
struct DeleteArray
{void operator()(T* ptr){delete[] ptr;}
}std::shared_ptr<ListNode> p2(new ListNode[10],DeleteArray<ListNode>());

2、自定义删除器是Lambda表达式

std::shared_ptr<ListNode> p3(fopen("Test.cpp","r"),[](FILE* ptr){fclose(ptr); });

简单实现

template<class T>
class shared_ptr
{
public:template<class D>
shared_ptr(T* ptr, D del)//接收自定义删除器的构造函数:_ptr(ptr), _pcount(new int(1)), _del(del)//新定义一个成员用于存放删除器{}//删除函数
void release()
{// 说明最后一个管理对象析构了,可以释放资源了if (--(*_pcount) == 0){cout << "delete:" << _ptr << endl;//delete _ptr;_del(_ptr);//将执行资源的指针也给删除器一份,从而使删除器开始运行delete _pcount;}
}private:D _val;//shared_ptr的官方实现的模板中没有第二个模板参数,所以这种写法是错误的
}

//利用function将删除器的类型进行封装
//删除器的返回类型肯定是void,接收的参数类型肯定是T*
function<void(T*)> _del;
​
//为了防止使用无自定义删除器的构造函数时,没有del的传入导致的_del为空的情况,所以我们要提供一个默认的删除方式:
function<void(T*)> _del = [](T* ptr) {delete ptr; };

~over~

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

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

相关文章

生成对抗网络——CGAN(代码+理解)

目录 一、CGAN模型介绍 二、CGAN训练流程 1. 初始化 2. 数据准备 3. 输出模型计算结果 4. 计算损失 5. 反向传播和优化 6. 迭代训练 三、CGAN实现 1. 模型结构 &#xff08;1&#xff09;生成器&#xff08;Generator&#xff09; &#xff08;2&#xff09;判别器…

ShuffleNet系列论文阅读笔记(ShuffleNetV1和ShuffleNetV2)

目录 ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices摘要Approach—方法Channel Shuffle for Group Convolutions—用于分组卷积的通道重排ShuffleNet Unit—ShuffleNet单元Network Architecture—网络体系结构 总结 ShuffleNet V2: Pra…

Vmware与Windows之间复制、粘贴内容、拖拽文件

Vmware17.0Ubuntu20 Vmware正确安装完linux虚拟机之后&#xff0c;这里以Ubuntu为例&#xff08;其他linux或windows系统也是类似的&#xff09;&#xff0c;如果你使用的默认配置&#xff0c;正常情况下就可以复制、粘贴和拖拽内容的&#xff0c;双方向都是支持的。如果不能复…

nvdiadocker相关配置S3Gaussian

https://download.csdn.net/download/sinat_21699465/89458214 dockerfile文件参考&#xff1a; https://download.csdn.net/download/sinat_21699465/89458214 prework&#xff1a; 显卡驱动决定了cuda版本支持的上限。例如nvdia535驱动最高支持cuda12.2所以显卡驱动版本选…

15.树形虚拟列表实现(支持10000+以上的数据)el-tree(1万+数据页面卡死)

1.问题使用el-tree渲染的树形结构&#xff0c;当数据超过一万条以上的时候页面卡死 2.解决方法&#xff1a; 使用vue-easy-tree来实现树形虚拟列表&#xff0c;注意&#xff1a;vue-easy-tree需要设置高度 3.代码如下 <template><div class"ve-tree" st…

2024广东省职业技能大赛云计算赛项实战——OpenStack搭建

OpenStack搭建 前言 搭建采用双节点安装&#xff0c;即controller控制节点和compute计算节点。 CentOS7 系统选择 2009 版本&#xff1a;CentOS-7-x86_64-DVD-2009.iso 可从阿里镜像站下载&#xff1a;https://mirrors.aliyun.com/centos/7/isos/x86_64/ OpenStack使用竞赛培…

JaveEE进阶----Spring Web MVC入门

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、什么是 Spring Web MVC&#xff1f;&#xff1f;1.1MVC 定义1.2 什么是Spring MVC ?1.3过浏览器和用户程序交互 二、 RequestMapping 注解三、Postman 前言…

容器基本概念_从虚拟化技术_到容器化技术_开通青云服务器_并远程连接_容器安装---分布式云原生部署架构搭建007

这一部分,属于以前都会用到的,会快速过一遍,对于关键技术问题会加以说明 https://www.yuque.com/leifengyang/oncloud文档地址在这里,可以看,有些命令可以复制使用 可以看到容器的出现就是 目的就是,让你做的所有的软件,都可以一键部署启动 打包就是docker build 然后: 对于…

陈晓婚前婚后大变样

陈晓婚前婚后大变样&#xff1f;陈妍希揭秘甜蜜与现实的碰撞在娱乐圈的星光璀璨中&#xff0c;有一对夫妻总是津津乐道&#xff0c;那就是陈晓和陈妍希。他们的爱情故事&#xff0c;从荧幕到现实&#xff0c;一直备受关注。然而&#xff0c;近日陈妍希在节目中透露&#xff0c;…

差分总结(一维+二维)

差分&#xff0c;可以视作前缀和的逆运算。 前缀和用于去求一个区间段的和 差分用于改变一个区间的值&#xff08;比如说某个区间都加上或者减去一个数&#xff09; P2367 语文成绩 #include<bits/stdc.h> using namespace std; #define int long long int n,p; int a…

RabbitMQ 学习笔记

RabbitMQ学习笔记 一些概念 Broker &#xff1a;RabbitMQ服务。 virtual host&#xff1a; 其实就是分组。 Connection&#xff1a;连接&#xff0c;生产者消费者与Broker之间的TCP连接。 Channel&#xff1a;网络信道&#xff0c;轻量级的Connection&#xff0c;使用Chann…

2024广东省职业技能大赛云计算赛项实战——Minio服务搭建

Minio服务搭建 前言 这道题是比赛时考到的&#xff0c;没找到具体题目&#xff0c;但在公布的样题中找到了&#xff0c;虽然很短~ 使用提供的 OpenStack 云平台&#xff0c;申请一台云主机&#xff0c;使用提供的软件包安装部署 MINIO 服务并使用 systemctl 管理 Minio是一个…

HTML静态网页成品作业(HTML+CSS)——手机电子商城网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

python API自动化(Pytest+Excel+Allure完整框架集成+yaml入门+大量响应报文处理及加解密、签名处理)

1.pytest数据参数化 假设你需要测试一个登录功能&#xff0c;输入用户名和密码后验证登录结果。可以使用参数化实现多组输入数据的测试: 测试正确的用户名和密码登录成功 测试正确的用户名和错误的密码登录失败 测试错误的用户名和正确的密码登录失败 测试错误的用户名和密码登…

定时器-前端使用定时器3s轮询状态接口,2min为接口超时

背景 众所周知&#xff0c;后端是处理不了复杂的任务的&#xff0c;所以经过人家的技术讨论之后&#xff0c;把业务放在前端来实现。记录一下这次的离大谱需求吧。 如图所示&#xff0c;这个页面有5个列表&#xff0c;默认加载计划列表。但是由于后端的种种原因&#xff0c;这…

C++ | Leetcode C++题解之第171题Excel表列序号

题目&#xff1a; 题解&#xff1a; class Solution { public:int titleToNumber(string columnTitle) {int number 0;long multiple 1;for (int i columnTitle.size() - 1; i > 0; i--) {int k columnTitle[i] - A 1;number k * multiple;multiple * 26;}return num…

QT中利用QMovie实现动态加载效果

1、效果 2、代码 #include "widget.h" #include "ui_widget.h" #include <QLabel> #include <QMovie>

YOLOv10训练自己的数据集(图像目标检测)

目录 1、下载代码 2、环境配置 3、准备数据集 4、yolov10训练 可能会出现报错&#xff1a; 1、下载代码 源码地址&#xff1a;https://github.com/THU-MIG/yolov10 2、环境配置 打开源代码&#xff0c;在Terminal中&#xff0c;使用conda 创建虚拟环境配置 命令如下&a…

Python基础教程(二十五):内置函数整理

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

将AI带入企业,红帽选择了开源

伴随着生成式AI与大模型技术的飞速发展&#xff0c;业界人士对于生成式AI应用在企业的落地也愈发关注。 近日在2024红帽媒体Open讲上&#xff0c;红帽全球副总裁兼大中华区总裁曹衡康深入剖析了AI在混合云中的应用及其带来的资源利用最大化优势&#xff0c;并同与会媒体共同探讨…