【C++11(三)】智能指针详解--RAII思想循环引用问题
💓博主CSDN主页:杭电码农-NEO💓
⏩专栏分类:C++从入门到精通⏪
🚚代码仓库:NEO的学习日记🚚
🌹关注我🫵带你学习C++
🔝🔝

C++11
- 1. 前言
- 2. 为什么要有智能指针?
- 3. RAII思想以及智能指针的设计
- 4. C++智能指针的发展历史
- 5. shared_ptr模拟实现
- 6. shared_ptr的循环引用问题
- 7. 定制删除器
- 8. 总结以及拓展
1. 前言
相信学C++的同学或多或少的听说过
智能指针这个词,博主刚听见这个词时
,觉得它应该很复杂,并且很高大上,但不
管是多牛的东西,都是人写出来的,是可
学习的!不要怀着害怕的心理来学习它
本章重点:
本篇文章着重讲解智能指针的发展历史
中出现过的auto_ptr,unique_ptr以及主
角shared_ptr.并且会介绍什么是RAII思想
以及为什么要有智能指针这一话题,最后
会给大家分析shared_ptr的循环引用问题
以及定制删除器的基本概念
2. 为什么要有智能指针?
在写代码时,我们经常在堆上申请空间
但是偶尔会忘记释放空间,会造成内存
泄漏问题,当然,这不是最重要的,在某些
场景下即使你释放了也会有问题:
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;
}
在上面代码的这种场景中,不管是使用
new还是调用div函数都有抛异常的风险
并且程序一旦抛异常就会直接跳到catch
处,所以上面的代码一旦抛异常就代表着
delete p1和p2并不会执行,也就会出现
内存泄漏的问题!这个问题不使用智能
指针是很难解决的!!!
3. RAII思想以及智能指针的设计
RAII思想
RAII思想是一种 利用对象生命周期来控制程序资源 (如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象
这种做法有两种好处:
- 不需要显式地释放资源
- 对象所需的资源在其生命期内始终有效
智能指针的基本设计
现在我们来写一个类,构造函数的
时候创造资源,析构函数的时候释放
资源,当对象出了作用域会自动调用析构!
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr): _ptr(ptr)
{}
~SmartPtr()
{if(_ptr!=nullptr)delete _ptr;
}
T& operator*() {return *_ptr;}
T* operator->() {return _ptr;}
private:T* _ptr;
};
现在我们来使用一下它:
SmartPtr<int> sp1(new int(10));
*sp = 20;
当然,重载了->是给自定义类型用的
4. C++智能指针的发展历史
首先,我们要清楚智能指针的一个大坑
那就是当一个指针赋值给另外一个指针
时,我们需要的是浅拷贝,因为我们就是想
让两个指针指向同一块空间,但是指向了
同一块空间就会有析构函数调用两次的风险
由于这一个大坑,智能指针进行了很多次迭代
- 在C++98的时候就已经在库中实现
了智能指针了,它就是auto_ptr

既然智能指针是随着历史不断发展的
就证明它前面的版本写的不咋滴[doge]
事实也是如此,auto_ptr是这样实现的,
既然有析构两次的风险,那么当我把A
指针赋值给B指针后,A指针就销毁不能用
了,对于不了解auto_ptr的人来说这无疑是
一个巨大的风险!
auto_ptr<int> ap1(new int(10));
auto_ptr<int> ap2(ap1);
//此时ap1已经失效了!
- 有了这一大坑后,C++11推出了全新
的智能指针:unique_ptr

unique_ptr的做法比auto_ptr还绝
智能指针不是拷贝有问题吗?那么
unique_ptr就禁用了拷贝和赋值,
很显然这也是一个坑,但是在实际
场景下,unique_ptr至少还能被用到
但auto_ptr是很多公司明令禁止使用的!
unique_ptr<int> up1(new int(10));
unique_ptr<int> up2(up1);//这里会直接报错
- 经过两次失败的智能指针后,C++11
还推出了今天的主角:shared_ptr

shared_ptr可堪称完美的智能指针
也是实际中使用的最多的智能指针
它采用的是引用计数的思想,当指向
这份空间的计数是1时才析构,大于1
时就将计数减一,非常的优雅!
由于智能指针在面试时让手撕的概率很大
所以我们会模拟实现它
5. shared_ptr模拟实现
我们使用引用计数的方式来实现
shared_ptr,也就是在原先代码的
基础上增加一个int*成员变量来保存
还有几个指针指向当前空间!
template<class T>
class Smart_Ptr //实现的C++11的shared_ptr版本
{
public:Smart_Ptr(T* ptr = nullptr):_ptr(ptr),_pcount(new int(1)){}~Smart_Ptr(){Release();}Smart_Ptr(const Smart_Ptr<T>& sp):_ptr(sp._ptr),_pcount(sp._pcount){Addcount();}Smart_Ptr<T>& operator=(const Smart_Ptr<T>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pcount = sp._pcount;Addcount();}return *this;}void Release(){if (--(*_pcount) == 0)//销毁最后一个变量时才释放资源{delete _ptr;delete _pcount;delete _pmtx;}}void Addcount(){(*_pcount)++;}void Subcount(){Release();private:T* _ptr;int* _pcount;
};
我们将计数++贺计数- -特意的提出来
这是因为很多场景下都需要这两个函数.
当计数不为1时就- -计数,当计数为一才
释放资源,并且这样写的好处是相同类型
的指针对象即使指向不同的空间也不会
出错,相反,使用static定义成员指针变量
就会出现上面的这种问题!
6. shared_ptr的循环引用问题
请看下面的代码运行会崩溃:
struct ListNode
{int _data;shared_ptr<ListNode> prev;shared_ptr<ListNode> next;~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);node1->next = node2;node2->prev = node1;return 0;
}
为啥会崩溃?下面我用画图加文字
的方式帮大家分析一下此问题:

现在来进一步分析:当main函数调用完,
node2会先析构,但是此时引用计数是2
所以不会释放空间而是将计数变为1.
然后node1再析构,同上,它的引用计数
也减为一,但是这两份空间并不会释放,
因为要node2的prev释放后,node1的空间
才会释放,那node2的prev什么时候释放?
答案是node2这份空间释放了才会释放
prev,那么node2这份空间什么时候释放?
答案是node1的next释放了它才释放,这
就形成了一个死循环,我等你释放了我才
能释放,对方也在等我释放了对方才能
释放,这就是"循环引用问题"
最好的解决方案就是在使用智能指针
的时候跳过这个坑,不用将智能指针和
这种场景一起使用!!!

7. 定制删除器
使用智能指针时可能会遇见下面的问题:
shared_ptr<int> sp1(new int[10]);
当变量出作用域销毁时即报错
因为new []对应的是delete [].
然而库中写法并不能识别有没有[]
还有一些问题:
shared_ptr<FILE> sp3(fopen("Test.cpp", "r"));
此时智能指针管理的对象并不是堆上
开辟的空间,delete完全没法用,此时需
要使用fclose,所以定制删除器非常重要

在构造函数的地方可以传入一个定制
删除器,也就是一个函数对象,此函数
中有对应的删除方法,请看下面的代码:
shared_ptr<int> sp2(new int[10], [](int* ptr) {delete[] ptr; });
shared_ptr<FILE> sp3(fopen("Test.cpp", "r"), [](FILE* ptr) {fclose(ptr); });
注:定制删除器属于了解的部分
8. 总结以及拓展
智能指针在面试中是常客,经常会被
问到发展历史和shared_ptr的手撕,
学到这里后,C++的所有重要的知识
差不多已经完结了,后面文章更新会慢一点
拓展:weak_ptr的拓展阅读
既然weak_ptr可以解决shared_ptr的
循环引用问题,那么什么是weak_ptr?
有兴趣的同学可以阅读下面这篇文章:
weak_ptr详解
相关文章:
【C++11(三)】智能指针详解--RAII思想循环引用问题
💓博主CSDN主页:杭电码农-NEO💓 ⏩专栏分类:C从入门到精通⏪ 🚚代码仓库:NEO的学习日记🚚 🌹关注我🫵带你学习C 🔝🔝 C11 1. 前言2. 为什么要有智能指针?3. RAII思想…...
佳明(Garmin) fēnix 7X 增加小睡检测功能
文章目录 (一)零星小睡(二)小睡检测(三)吐槽佳明(3.1)心率检测(3.2)光线感应器(3.3)手表重量(3.4)手表续航 &a…...
二、如何保证架构的质量、架构前期准备、技术填补与崩溃预防、系统重构
1、如何保证架构的质量 -- 稳定性和健壮性 2、正确的选择是良好的开端 -- 架构前期准备 ① 架构师分类:系统架构师、应用架构师、业务架构师 3、技术填补与崩溃预防 4、系统重构...
14、SQL注入——HTTP文件头注入
文章目录 一、HTTP Header概述1.1 HTTP工作原理1.2 HTTP报文类型1.3 较重要的HTTP Header内容 二、HTTP Header注入2.1 HTTP Header注入的前提条件2.2 常见的HTTP Header注入类型 一、HTTP Header概述 1.1 HTTP工作原理 1.2 HTTP报文类型 (1)请求报文 …...
李宏毅bert记录
一、自监督学习(Self-supervised Learning) 在监督学习中,模型的输入为x,若期望输出是y,则在训练的时候需要给模型的期望输出y以判断其误差——有输入和输出标签才能训练监督学习的模型。 自监督学习在没有标注的训练…...
.Net6.0 Microsoft.AspNetCore.Http.Abstractions 2.20 已弃用
您想要升级 Microsoft.AspNetCore.Http.Abstractions 包,您需要注意以下几点: Microsoft.AspNetCore.Http.Abstractions 包在 ASP.NET Core 2.2 版本后已经被标记为过时,因为它已经被包含在 Microsoft.AspNetCore.App 框架引用中12。因此&am…...
c2-C语言--指针
1.用一级指针遍历一维数组 结论 buf[i]<>*(buf i) <> *(p i)<> p[i] #include <stdio.h>int main(){int buf[5] {10,20 ,30 ,40,50}; //buf[0] --- int // buf --&buf[0] ----int *int *p buf;//&buf[0] --- &*(buf0)printf(&quo…...
kafka入门(四):消费者
消费者 (Consumer ) 消费者 订阅 Kafka 中的主题 (Topic) ,并 拉取消息。 消费者群组( Consumer Group) 每一个消费者都有一个对应的 消费者群组。 一个群组里的消费者订阅的是同一个主题,每个消费者接收主题的一部分分区的消息…...
DFS、BFS求解leetcode图像渲染问题(Java)
目录 leetcode733题.图像渲染 DFS BFS leetcode733题.图像渲染 733. 图像渲染 - 力扣(LeetCode) 有一幅以 m x n 的二维整数数组表示的图画 image ,其中 image[i][j] 表示该图画的像素值大小。 你也被给予三个整数 sr , sc 和 newColor …...
0基础学习云计算难吗?
很多人经常会问云计算是什么?云计算能干什么?学习云计算能做什么工作?其实我们有很多人并不知道云计算是什么,小知今天来给大家讲讲学习云计算能做什么。 中国的云计算行业目前正处于快速发展阶段,随着互联网和数字化…...
【RabbitMQ高级功能详解以及常用插件实战】
文章目录 队列1 、Classic经典队列2、Quorum仲裁队列3、Stream流式队列4、如何使用不同类型的队列 二、死信队列 队列 classic经典队列,Quorum仲裁队列,Stream流式队列 1 、Classic经典队列 这是RabbitMQ最为经典的队列类型。在单机环境中,…...
开源的数据流技术,该选择Redpanda还是Apache Kafka?
本文将比较Apache Kafka和Redpanda两种开源的数据流技术,在云原生实时处理能力上的不同,以及如何在项目中做出选择。 目前,Apache Kafka不但成为了数据流处理领域事实上的标准,而且带动了同类产品的出现。Redpanda就是其中之一…...
720度vr虚拟家居展厅提升客户的参观兴致
VR虚拟展厅线上3D交互展示的优势有以下几点: 打破了场馆的展示限制,可展示危险性制品、珍贵稀有物品、超大型设备等,同时提供了更大的展示空间和更丰富的展示内容。 可提供企业真实环境的实时VR全景参观,提升潜在客户信任度。 提供…...
mysql中的DQL查询
表格为: DQL 基础查询 语法:select 查询列表 from 表名:(查询的结果是一个虚拟表格) -- 查询指定的列 SELECT NAME,birthday,phone FROM student -- 查询所有的列 * 所有的列, 查询结果是虚拟的表格&am…...
【数据结构高阶】红黑树
目录 一、红黑树的概念 二、红黑树的性质 2.1 红黑树与AVL树的比较 三、红黑树的实现 3.1 红黑树节点的定义 3.2 数据的插入 3.2.1 红黑树的调整思路 3.2.1.1 cur为红,f为红,g为黑,u存在且为红 3.2.1.2 cur为红,f为红&am…...
Unity中Batching优化的GPU实例化(1)
文章目录 前言一、GPU实例化的规则1、网格一样,材质一样,但是材质属性不一样2、单个合批最大上限为511个对象3、只有OpenGL es 3.0及以上才支持(3.0及以上有部分硬件可能也不支持) 二、GPU实例化的应用场景1、公开几个成员属性&am…...
vue的data
类型:Object | Function 限制:组件的定义只接受 function。 详细: Vue 实例的数据对象。Vue 会递归地把 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个…...
Java基础课的中下基础课04
目录 二十三、集合相关 23.1 集合 (1)集合的分支 23.2 List有序可重复集合 (1)ArrayList类 (2)泛型 (3)ArrayList常用方法 (4)Vector类 (…...
解决vue ssr服务端渲染运行时报错:net::ERR_PROXY_CONNECTION_FAILED
现象: 从代码里找了半天也没有找到问题,但是由于ssr服务端渲染配置本身非常复杂,步骤又繁琐, 而且报错又很多,不知道哪里出了问题。 感觉是header或者cookie丢失造成的,因为据说ssr本身有这样的缺陷&…...
APIFox:打造高效便捷的API管理工具
随着互联网技术的不断发展,API(应用程序接口)已经成为了企业间数据交互的重要方式。然而,API的管理和维护却成为了开发者们面临的一大挑战。为了解决这一问题,APIFox应运而生,它是一款专为API管理而生的工具…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...
Android屏幕刷新率与FPS(Frames Per Second) 120hz
Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数,单位是赫兹(Hz)。 60Hz 屏幕:每秒刷新 60 次,每次刷新间隔约 16.67ms 90Hz 屏幕:每秒刷新 90 次,…...
【学习记录】使用 Kali Linux 与 Hashcat 进行 WiFi 安全分析:合法的安全测试指南
文章目录 📌 前言🧰 一、前期准备✅ 安装 Kali Linux✅ 获取支持监听模式的无线网卡 🛠 二、使用 Kali Linux 进行 WiFi 安全测试步骤 1:插入无线网卡并确认识别步骤 2:开启监听模式步骤 3:扫描附近的 WiFi…...
【JavaEE】万字详解HTTP协议
HTTP是什么?-----互联网的“快递小哥” 想象我们正在网上购物:打开淘宝APP,搜索“蓝牙耳机”,点击商品图片,然后下单付款。这一系列操作背后,其实有一个看不见的“快递小哥”在帮我们传递信息,…...
codeforces C. Cool Partition
目录 题目简述: 思路: 总代码: https://codeforces.com/contest/2117/problem/C 题目简述: 给定一个整数数组,现要求你对数组进行分割,但需满足条件:前一个子数组中的值必须在后一个子数组中…...
运动控制--BLDC电机
一、电机的分类 按照供电电源 1.直流电机 1.1 有刷直流电机(BDC) 通过电刷与换向器实现电流方向切换,典型应用于电动工具、玩具等 1.2 无刷直流电机(BLDC) 电子换向替代机械电刷,具有高可靠性,常用于无人机、高端家电…...
