c++--智能指针(RAII)

news2024/9/20 5:36:25

智能指针可以帮助我们管理动态空间,即自动释放动态空间。

---------------------------------------------------------------------------------------------------------------------------------

简单原理

事实上,智能指针的原理就是将指向动态空间的指针托管给一个自定义对象(把这个指针作为成员变量),然后在析构函数中将动态空间释放。这样就实现了动态空间的生命周期与对象的生命周期相同。如果要使用这个指针,我们只需要在类里面重载*,->等运算符或者写成员函数即可。

---------------------------------------------------------------------------------------------------------------------------------

智能指针的历史及主要困难

智能指针在c++98中就已经被引入,即现在c++库中的auto_ptr类模版。但是它在拷贝时有一个缺陷:

为什么报错呢?本来我们想要x和y共同管理一块动态空间,但是因为 auto_ptr 中的拷贝实际上是管理权转移,也就是说,当把x拷贝给y的时候,x就没有了该动态空间管理权(存放动态空间指针的成员变量被置为空了),所以x->year++实际上是对空指针操作,所以会报错。

为什么非要实现拷贝呢?不让它拷贝行吗?不行,我们总有一些情况是需要拷贝的,比如说:

push_back中就会拷贝(赋值拷贝),但是拷贝就会产生上述问题。还有传参等情况。

那么库里面为什么不能不把x置为空?这样也不行,因为会导致被管理的指针析构两次而报错。

这就是智能指针的困难

可以看出auto_ptr是不成熟的,但是如果在库里删除它会让一些已存在的使用了它程序无法运行。为了避免像auto_ptr这种尴尬的情况,后续委员会成员先在boost库(c++扩展库)写功能,如果好用的话才纳入c++标准库。boos库中就有新的好用的智能指针shared_ptr,unique_str(简单粗暴禁止拷贝,适用面不广)等,他们在c++11中被纳入标准库。

C++11和boost中智能指针的关系

1. C++ 98 中产生了第一个智能指针auto_ptr.

2. C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.

3. C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。

4. C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost 的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的

---------------------------------------------------------------------------------------------------------------------------------

shared_ptr

这个是比较好用的一种智能指针。它通过引用计数的方式解决拷贝问题。

原理:对于管理同一份资源的智能指针,不在采用管理权转移的方式,让他们都有管理权,这样会导致析构两次,所以我们再让这些智能指针管理同一个计数器,每多一个智能指针管理这个资源,计数器加1,每析构一个智能指针,计数器减1。如果计数器成为0,说明这个智能指针是唯一的管理者,此时如果它析构,我们就把资源也释放了。

这种方式防止了析构多次以及同一份资源不能被多个智能指针管理的缺陷,俗称“最后一个离开房间的人关灯”

---------------------------------------------------------------------------------------------------------------------------------

接下来简单实现一下share_ptr

1.

这是类模版的头,T用来接收任意类型的资源

2.

_ptr指向存放动态资源的指针。

_count是指向计数器。管理同一个资源的智能指针中,这个指针指向的计数器相同。这个计数器是动态开辟的,在资源释放的时候将他也释放。我们给它缺省值为1。

_delete是删除器。智能指针指向的资源不光是一个动态开辟的变量(用delete释放),也有可能是FILE类型(用fclose释放),还有可能是动态数组类型,用delete[]释放,总之释放资源的方式是不同的,但我们写的模版要使用所有类型,所以我们在用智能指针管理资源的时候需要传入一个可调用对象(删除器),通过传入不同的删除器,实现资源的正确释放。至于为什么用包装器(function<void(T*)>),我们稍后再说。

3.

这是类模版的构造函数。

第一个函数是一个函数模版D是函数模版参数,由你传入的对象类型决定,T是类模版参数,由类决定。我们知道删除器是要释放资源,而释放资源是在析构函数内部,也就是说,我们要在析构函数内部使用D类型的对象。在一个函数传入,在另一个函数使用,如何做呢?他两共有的可以访问的是成员变量,所以我们可以用成员变量来接收这一对象,然后另一个函数就可以使用了。

那么问题又来了,这个成员变量应该是什么类型呢?是D,但问题是,D是函数模版参数,类模版没有这个模版参数,它不认识D,这时候就需要包装器了,它可以包装任何可调用对象,且只需要返回值和参数类型,参数类型就是指向资源的指针T*,返回值类型就是void

function<void(T*)> _delete = [](T* x)->void {delete x; };

而[](T* x)->void {delete x; }是一个lambda表达式,它作为删除器的缺省值,使得不传删除器的时候也可以有默认删除器使用。

至于为什么不直接把删除器设计成类模版参数,这是因为我们要模拟库的实现,哪种都可以的。

第二个函数是普通的构造函数

4.

这是析构函数。智能指针析构时,要先让count--,然后判断是否需要释放资源,也就是count是否为0(即这是最后一个管理这个资源的智能指针)。

而我把逻辑封装在release函数里。

5.

这是shared_ptr的关键,拷贝构造和赋值拷贝。

一但拷贝构造或者拷贝赋值,说明多了一个智能指针管理相同的资源,所以_count++。

对于拷贝复制,先得把该智能指针原来管理的资源清理(复用 release),然后接收新的资源。

6.

这是关于被管理资源的使用函数。const需要加上,以便const对象和非const对象都能调用这些函数,可以自己写去体会一下为什么要加。

---------------------------------------------------------------------------------------------------------------------------------

shared_ptr的循环引用

这是一个典型例子,下面是分析

这是shared_ptr的缺陷!

解决方法:

仿造库里面的weak_ptr,我们设计出自己的简化的weak_ptr(库里面要复杂许多倍)。

把节点改成这样:

用weak_ptr代替节点中的shared_ptr,shared_ptr可以赋值给weak_ptr(通过weak_ptr的赋值拷贝),weak_ptr管理一个资源不会增加计数器。这样,x->_next = y,y->_prev = x 后,count1,count2还是1而不是2,此时在走一遍上述过程,你会发现循环引用已经破除了。

---------------------------------------------------------------------------------------------------------------------------------

对了,weak_ptr可以通过lock函数恢复到shared_ptr,这里只是简单介绍智能指针,说的不全面,见谅

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

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

相关文章

一目了然的图解一般AI与AI Agent到底区别在哪

全部使用Midjourney绘成&#xff0c;绘制魔法放出自取 魔咒1 Lego shaped Skywalker Luke and Lego shaped Anakin battle --niji 6 --ar 1:1 魔咒2 Lego-style Luke Skywalker and Lego-style Anakin are sitting in a caf talking. --niji 6 --ar 1:1 魔咒3 Anakin in …

18、Gemini-Pentest-v2

难度 中 目标 root权限 一个flag 靶机启动环境为VMware kali 192.168.152.56 靶机 192.168.152.63 信息收集 web测试 访问80端口 上面介绍了一下这个系统是一个内部系统&#xff0c;让员工查看他们的个人资料还可以导出为PDF 页面还有一个链接是UserList可以访问但是页面什…

【自然语言处理】调用NLTK数据失败‘wordnet‘和‘punkt‘不存在[Errno 11004]问题解决

wordnet报错 明明已经按照了nltk包&#xff0c;但使用 WordNet 语料库时依然报错提示数据不存&#xff0c;依据以下代码在python中下载wordnet仍然报错&#xff1a; import nltk nltk.download(wordnet)运行后始终提示&#xff1a; [nltk_data] Error loading wordnet: <…

【算法】PageRank

一、引言 PageRank是由谷歌创始人拉里佩奇和谢尔盖布林在斯坦福大学读研究生时发明的一种算法&#xff0c;用于衡量网页的重要性。它基于一个简单的假设&#xff1a;更重要的网页会有更多的链接指向它。 二、算法原理 PageRank算法的核心思想是&#xff0c;一个网页的重要性可以…

如何找到适合的IT外包服务商

在信息技术迅速发展的今天&#xff0c;IT外包服务已成为企业运营中不可或缺的一部分。选择合适的IT外包服务商对于确保项目成功、提高效率和降低成本至关重要。下面一起探讨评估和选择IT外包服务商的关键因素。 关键因素一&#xff1a;专业资质与认证 选择IT外包服务商时&…

ROS 工具箱系统要求

ROS 工具箱系统要求 要为 ROS 或 ROS 2 生成自定义消息&#xff0c;或从 MATLAB 或 Simulink 软件中部署 ROS 或 ROS 2 节点&#xff0c;您必须构建必要的 ROS 或 ROS 2 软件包。要构建这些软件包&#xff0c;您必须具备 Python 软件、CMake 软件以及适用于您的平台的 C 编译器…

分支和循环以及猜数字游戏的实现

分支和循环以及猜数字游戏的实现目录 随机书生成randsrandtime设置随机数的范围 猜数字游戏的实现 随机书生成 rand C语言中有一个函数叫rand函数&#xff0c;它可以生成随机数&#xff0c;代码格式如下&#xff1a; int rand&#xff08;void&#xff09;rand函数会返回一个…

Unity(2022.3.41LTS) - UI详细介绍- Button(按钮)TMP

目录 零.简介 一、基本功能与重要性 二、属性和设置详解 三、使用方法深入探讨 四、优化和注意事项 零.简介 在 Unity 中&#xff0c;按钮&#xff08;Button&#xff09;是用户界面中非常重要的交互元素之一。以下是对 Unity 中按钮的更详细介绍&#xff1a; 一、基本功…

【docker】docker 是什么

docker 是什么 Docker 本质Docker 的引擎迭代Docker 和虚拟机的区别Docker 为什么比虚拟机资源利用率高&#xff0c;启动快Docker 和 JVM 虚拟化的区别&#xff1f; Docker 本质 Docker 本质其实是 LXC 之类的增强版&#xff0c;它本身不是容器&#xff0c;而是容器的易用工具…

《黑神话:悟空》背后的渲染技术解析

《黑神话&#xff1a;悟空》作为备受瞩目的国产单机游戏&#xff0c;承载了深厚的文化底蕴&#xff0c;其背后的渲染技术无疑是推动其视觉表现达到新高度的关键。这款游戏不仅融合了传统与创新的角色设计&#xff0c;还通过一系列前沿的图形渲染技术&#xff0c;为玩家带来了前…

JVM面试(三)类加载过程

什么是类加载&#xff1f; 前面的文章已经说过&#xff0c;我们手动敲代码&#xff0c;写出来的文件是.java文件。 虚拟机编译之后&#xff0c;可识别的文件是.class文件。 但是在真正运行的时候&#xff0c;在内存中进行各种流转&#xff0c;通过程序来进行执行的时候&#…

【深度解析】GPT-3.5、GPT-4.0、GPT-4o mini的区别,你了解多少?

在人工智能的浪潮中&#xff0c;GPT系列模型如同璀璨的明星&#xff0c;吸引着无数开发者和研究者的目光。随着技术的不断进步&#xff0c;OpenAI推出了多个版本的GPT模型&#xff0c;其中GPT-3.5、GPT-4.0、GPT-4o mini各具特色。今天&#xff0c;我们就来深入探讨它们之间的区…

Odoo:开源免费的OMS订单管理系统

企业全渠道订单管理服务平台 开源智造Odoo免费开源ERP全渠道订单管理应用包含渠道分销订单、零售订单、电商订单、直销订单四部分&#xff0c;主要服务于核心企业的渠道信息化管理&#xff0c;帮助企业从传统分销向移动互联、电商直销或渠道数字化订货转型、新零售转型&#xf…

笔记 10 : 彭老师课本第 5 章 , GPIO , vim 的复制剪切删除 ; uboot读写内存的 md , nm 命令

&#xff08;77&#xff09; 进入代码实验以来&#xff0c;开始有机会频繁的修改源代码。补充 vim 的使用&#xff1a; &#xff08;78&#xff09; 如何只查看某个文件的权限&#xff0c;属性&#xff1a; &#xff08;79&#xff09; 关于 rm 命令&#xff1a; &#xff08;…

【netty系列-09】深入理解和解决tcp的粘包拆包

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…

C#——XML序列化

开发环境 VS2022 .net core 6.0 序列化概念 序列化是将内存中的对象或者对象图&#xff08;一组相互引用的对象&#xff09;拉平为一个可以保存或进行传输的字节流&#xff0c;或者XML节点。反序列化正好相反&#xff0c;它把数据流重新构造成内存中的一个对象或者对象图。…

【位运算】--- 进阶题目赏析

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Journey 本节我们来赏析位运算的一些进阶题目。 &#x1f3e0; 只出现一次的数字II &#x1f4cc; 题目解析 只出现一次的数字II &#x1f4cc; 算…

Anylogic制作界面元素tips

点击元素后跳转至其他视图&#xff0c;且能够把某个共同元素移植过去 navigate( viewStatistics2 ); groupControls.setX( groupControls.getX() 1200 );

python-笨小猴

题目描述 笨小猴的词汇量很小&#xff0c;所以每次做英语选择题的时候都很头疼。但是他找到了一种方法&#xff0c;经试验证明&#xff0c;用这种方法去选择选项的时候选对的几率非常大&#xff01; 这种方法的具体描述如下&#xff1a;假设maxn是单词中出现次数最多的字母的出…

kafka3.7.1 单节点 KRaft部署测试发送和接收消息

一、环境准备 kafka3.7.1 包下载地址&#xff1a; https://mirrors.nju.edu.cn/apache/kafka/3.7.1/kafka_2.13-3.7.1.tgz openjdk11.0.2 下载地址&#xff1a; https://mirrors.nju.edu.cn/openjdk/11.0.2/openjdk-11.0.2_linux-x64_bin.tar.gz 二、openjdk 安装 【如已安装…