当前位置: 首页 > news >正文

【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思想以及智能指针的设计

  1. RAII思想

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

这种做法有两种好处:

  • 不需要显式地释放资源
  • 对象所需的资源在其生命期内始终有效
  1. 智能指针的基本设计

现在我们来写一个类,构造函数的
时候创造资源,析构函数的时候释放
资源,当对象出了作用域会自动调用析构!

// 使用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++智能指针的发展历史

首先,我们要清楚智能指针的一个大坑
那就是当一个指针赋值给另外一个指针
时,我们需要的是浅拷贝,因为我们就是想
让两个指针指向同一块空间,但是指向了
同一块空间就会有析构函数调用两次的风险
由于这一个大坑,智能指针进行了很多次迭代

  1. 在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已经失效了!
  1. 有了这一大坑后,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);//这里会直接报错
  1. 经过两次失败的智能指针后,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++异常的处理方式🔍

相关文章:

【C++11(三)】智能指针详解--RAII思想循环引用问题

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; C11 1. 前言2. 为什么要有智能指针?3. RAII思想…...

佳明(Garmin) fēnix 7X 增加小睡检测功能

文章目录 &#xff08;一&#xff09;零星小睡&#xff08;二&#xff09;小睡检测&#xff08;三&#xff09;吐槽佳明&#xff08;3.1&#xff09;心率检测&#xff08;3.2&#xff09;光线感应器&#xff08;3.3&#xff09;手表重量&#xff08;3.4&#xff09;手表续航 &a…...

二、如何保证架构的质量、架构前期准备、技术填补与崩溃预防、系统重构

1、如何保证架构的质量 -- 稳定性和健壮性 2、正确的选择是良好的开端 -- 架构前期准备 ① 架构师分类&#xff1a;系统架构师、应用架构师、业务架构师 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报文类型 &#xff08;1&#xff09;请求报文 …...

李宏毅bert记录

一、自监督学习&#xff08;Self-supervised Learning&#xff09; 在监督学习中&#xff0c;模型的输入为x&#xff0c;若期望输出是y&#xff0c;则在训练的时候需要给模型的期望输出y以判断其误差——有输入和输出标签才能训练监督学习的模型。 自监督学习在没有标注的训练…...

.Net6.0 Microsoft.AspNetCore.Http.Abstractions 2.20 已弃用

您想要升级 Microsoft.AspNetCore.Http.Abstractions 包&#xff0c;您需要注意以下几点&#xff1a; Microsoft.AspNetCore.Http.Abstractions 包在 ASP.NET Core 2.2 版本后已经被标记为过时&#xff0c;因为它已经被包含在 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) &#xff0c;并 拉取消息。 消费者群组&#xff08; Consumer Group&#xff09; 每一个消费者都有一个对应的 消费者群组。 一个群组里的消费者订阅的是同一个主题&#xff0c;每个消费者接收主题的一部分分区的消息…...

DFS、BFS求解leetcode图像渲染问题(Java)

目录 leetcode733题.图像渲染 DFS BFS leetcode733题.图像渲染 733. 图像渲染 - 力扣&#xff08;LeetCode&#xff09; 有一幅以 m x n 的二维整数数组表示的图画 image &#xff0c;其中 image[i][j] 表示该图画的像素值大小。 你也被给予三个整数 sr , sc 和 newColor …...

0基础学习云计算难吗?

很多人经常会问云计算是什么&#xff1f;云计算能干什么&#xff1f;学习云计算能做什么工作&#xff1f;其实我们有很多人并不知道云计算是什么&#xff0c;小知今天来给大家讲讲学习云计算能做什么。 中国的云计算行业目前正处于快速发展阶段&#xff0c;随着互联网和数字化…...

【RabbitMQ高级功能详解以及常用插件实战】

文章目录 队列1 、Classic经典队列2、Quorum仲裁队列3、Stream流式队列4、如何使用不同类型的队列 二、死信队列 队列 classic经典队列&#xff0c;Quorum仲裁队列&#xff0c;Stream流式队列 1 、Classic经典队列 这是RabbitMQ最为经典的队列类型。在单机环境中&#xff0c…...

开源的数据流技术,该选择Redpanda还是Apache Kafka?

本文将比较Apache Kafka和Redpanda两种开源的数据流技术&#xff0c;在云原生实时处理能力上的不同&#xff0c;以及如何在项目中做出选择。 目前&#xff0c;Apache Kafka不但成为了数据流处理领域事实上的标准&#xff0c;而且带动了同类产品的出现。Redpanda就是其中之一…...

720度vr虚拟家居展厅提升客户的参观兴致

VR虚拟展厅线上3D交互展示的优势有以下几点&#xff1a; 打破了场馆的展示限制&#xff0c;可展示危险性制品、珍贵稀有物品、超大型设备等&#xff0c;同时提供了更大的展示空间和更丰富的展示内容。 可提供企业真实环境的实时VR全景参观&#xff0c;提升潜在客户信任度。 提供…...

mysql中的DQL查询

表格为&#xff1a; DQL 基础查询 语法&#xff1a;select 查询列表 from 表名&#xff1a;&#xff08;查询的结果是一个虚拟表格&#xff09; -- 查询指定的列 SELECT NAME,birthday,phone FROM student -- 查询所有的列 * 所有的列&#xff0c; 查询结果是虚拟的表格&am…...

【数据结构高阶】红黑树

目录 一、红黑树的概念 二、红黑树的性质 2.1 红黑树与AVL树的比较 三、红黑树的实现 3.1 红黑树节点的定义 3.2 数据的插入 3.2.1 红黑树的调整思路 3.2.1.1 cur为红&#xff0c;f为红&#xff0c;g为黑&#xff0c;u存在且为红 3.2.1.2 cur为红&#xff0c;f为红&am…...

Unity中Batching优化的GPU实例化(1)

文章目录 前言一、GPU实例化的规则1、网格一样&#xff0c;材质一样&#xff0c;但是材质属性不一样2、单个合批最大上限为511个对象3、只有OpenGL es 3.0及以上才支持&#xff08;3.0及以上有部分硬件可能也不支持&#xff09; 二、GPU实例化的应用场景1、公开几个成员属性&am…...

vue的data

类型&#xff1a;Object | Function 限制&#xff1a;组件的定义只接受 function。 详细&#xff1a; Vue 实例的数据对象。Vue 会递归地把 data 的 property 转换为 getter/setter&#xff0c;从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个…...

Java基础课的中下基础课04

目录 二十三、集合相关 23.1 集合 &#xff08;1&#xff09;集合的分支 23.2 List有序可重复集合 &#xff08;1&#xff09;ArrayList类 &#xff08;2&#xff09;泛型 &#xff08;3&#xff09;ArrayList常用方法 &#xff08;4&#xff09;Vector类 &#xff08;…...

解决vue ssr服务端渲染运行时报错:net::ERR_PROXY_CONNECTION_FAILED

现象&#xff1a; 从代码里找了半天也没有找到问题&#xff0c;但是由于ssr服务端渲染配置本身非常复杂&#xff0c;步骤又繁琐&#xff0c; 而且报错又很多&#xff0c;不知道哪里出了问题。 感觉是header或者cookie丢失造成的&#xff0c;因为据说ssr本身有这样的缺陷&…...

APIFox:打造高效便捷的API管理工具

随着互联网技术的不断发展&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为了企业间数据交互的重要方式。然而&#xff0c;API的管理和维护却成为了开发者们面临的一大挑战。为了解决这一问题&#xff0c;APIFox应运而生&#xff0c;它是一款专为API管理而生的工具…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...