【C++】智能指针

文章目录

  • 1. 为什么需要智能指针?
  • 2. 智能指针的使用
    • 智能指针的常见问题
      • 1.使用对象的生命周期去控制资源
      • 2. 像指针一样使用
      • 3. 拷贝问题
        • auto_ptr ——管理权转移
        • unique_ptr ——防拷贝
          • C++98版本
          • C++11版本
        • shared_ptr (根本解决拷贝问题)
          • 赋值
          • 代码实现
        • weak_ptr —— 循环引用

1. 为什么需要智能指针?

若p1处new抛异常,则相当于p1的new没有成功,则什么都不用做


若p2处new抛异常,则相当于p2的new没有成功,而p1的new成功了,所以需要释放p1,然后再重新抛出


若div处抛异常,则将p1与p2都释放,再将其重新抛出


可以看出处理起来非常麻烦,存在内存泄漏的问题(只进行new,但没有delete)
第二个new抛异常要释放第一个new,div抛异常要释放前两个new
若再添加一个new,则又会存在new抛异常的问题,还需添加 try catch

为了提前预防内存泄漏的问题,就提出了智能指针

2. 智能指针的使用

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:
不需要显式地释放资源
对象所需的资源在其生命期内始终保持有效

RAII是一种思想,智能指针是这种思想的产物

智能指针的常见问题

1.使用对象的生命周期去控制资源

创建一个私有的成员变量 _ptr指针
在构造函数时,将指针保存起来
在析构函数时,将指针释放

将申请的资源,交给智能指针对象去管理
(通过这个指针 去构造一个智能指针对象,这个对象会把指针保留起来)


创建对象时,会调用构造函数,将new int 传给类中的指针,对象会把指针保留起来
v1和v2属于局部对象,出了作用域时,就会调用析构函数 ,完成释放

若第一个new抛异常,就不会进入构造函数中
若第二个new抛异常,则调用析构,将第一个new释放掉
若div抛异常,则v1和v2对象都调用析构,将第一个new和第二个new都释放掉

通过类的构造和析构的自动调用,利用对象的生命周期来管理资源,被称之为 RAII

2. 像指针一样使用

在类中实现 operator() 和operator->,使对象可以进行解引用 和->访问成员的操作


3. 拷贝问题

因为没有在类中实现拷贝构造,默认是浅拷贝 ,所以就会导致释放两次,从而报错

深拷贝是不可以的,因为指针拷贝要的就是浅拷贝

链表等迭代器 结构与智能指针类似,用的是浅拷贝,为什么没有问题?
因为迭代器不管资源的释放,资源释放是容器处理的
智能指针需要管资源释放,所以不能单纯的浅拷贝

auto_ptr ——管理权转移

当上述v1和v2都管理这个资源就会有问题,两者都会去释放,导致释放两次


所以C++98版本的库中就提供了auto_ptr的智能指针,提出了 管理权转移的思想

官方文档:auto_ptr


将管理权只给一个对象,剩下一个对象去除管理权


如果不了解管理权转移的特性,就不知道v1已经为空,依旧对v1进行解引用,就会报错
因为管理权转移后,v1悬空不能访问

所以管理权转移,存在被拷贝对象悬空的问题

unique_ptr ——防拷贝

官方文档:unique_ptr


在C++98和C++11之间 产生了一个 库 boost (准标准库)
在boost中 就把智能指针的问题解决了
boost 中包含 scoped_ptr shared_ptr weak_ptr 体系

C++11将其吸收过来以后,将 scoped_ptr 改成 unique_ptr ,其他没变
即 unique_ptr shared_ptr weak_ptr
unique_ptr的特点为 简单 粗暴 防拷贝 (不需要拷贝的场景)


C++98版本

拷贝构造和赋值是默认成员函数,若自己不实现,会自动生成,所以必须写
但是写又不知道写什么,所以C++98思路是只声明,不实现

只在 类里面声明是不可以的,因为在类外可以实现
所以还要声明成私有

在这里插入图片描述

C++11版本

使用禁止生成默认函数的关键字 delete

在这里插入图片描述
不受公有 或者 私有的 影响

shared_ptr (根本解决拷贝问题)

官方文档:shared_ptr


特点为使用引用计数,支持拷贝

有两个对象指向资源,当析构时,会析构两次
为了解决这个问题,就增加一个引用计数,若只有一个对象,就为1,若为两个对象,则为2


当其中一个对象要析构时,就先看引用计数,若引用计数减1还大于0,就要什么都不管
若引用计数减1为0,则表示最后一个管理这块资源的对象,就可以将其释放掉


若将引用计数设置为静态
(静态的成员 是属于这个类的所有对象)


对象C指向与 对象A /B不同的资源,当对 C进行释放时,也会影响到对A和B的引用计数的值
所以不能使用 静态的引用计数


每个资源都应该配对一个引用计数


new一个引用计数
对象除了指向资源,也要指向引用计数


在构造时,先new一块空间,让_pcount指向这块空间


只有当引用计数 为0时,才会去析构 释放


拷贝构造是浅拷贝,通过引用计数的方式,每次有一个对象指向资源,就使引用计数+1

赋值

情况1

若将对象C赋值给对象A,则使对象A指向对象C的资源处 ,同时对象B的引用计数-1,对象C的引用计数+1


情况2

若将对象A赋值给对象C,则对象C原本指向的引用计数为0,该资源要被释放

代码实现

namespace yzq
{template<class T>class shared_ptr{public://构造shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)){}//析构~shared_ptr(){Release();}void Release(){//若引用计数-- 后为0,则说明为最后一个对象 就进行释放if (--(*_pcount) == 0){cout << "delete: " << _ptr << endl;//释放资源和引用计数delete _ptr;delete _pcount;}}void Addcount(){++(*_pcount);}//拷贝构造(浅拷贝)shared_ptr(const shared_ptr<T>&sp):_ptr(sp._ptr),_pcount(sp._pcount){//拷贝后将计数++Addcount();}//赋值shared_ptr<T>& operator=(const shared_ptr<T>&sp){//若指向同一块资源 就不需要赋值if (_ptr != sp._ptr){//被赋值的对象 引用计数--if (--(_pcount) == 0){//若为0,则释放资源和引用计数delete _ptr;delete _pcount;}//改变指向_ptr = sp._ptr;_pcount = sp._pcount;//改变指向后的对象 的引用计数++(*_pcount)++;}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get()//获取指针{return _ptr;}private:T* _ptr;int* _pcount;};void test_shared(){shared_ptr<int>v1(new int(1));shared_ptr<int>v2(v1);}} 

weak_ptr —— 循环引用

new出两个节点,将两个节点链接起来,再删除节点

但是n1与n2链接时,可能会抛异常,会没有释放
所以将其改为使用智能指针


使用智能指针就进行释放了


n1和n2作为智能指针对象,而next和prev作为原生指针
智能指针对象是没办法给原生指针的


将next和prev都转化为智能指针即可 解决问题
但是节点不释放了

节点使用原生指针可以释放,而使用智能指针不能释放,这样的问题被称为循环引用


n1与n2都是智能指针,分别去管理资源

n2对应的引用计数为1,将n1的_next指向n2,导致n2的引用计数加1


n1的引用计数为1,将n2的_prev指向n1,导致n1的引用计数加1


出了作用域,先将n2节点析构,使其引用计数减1,此时n2的引用计数为1,还有一个_next的智能指针指向n2,只有当_next析构n2才能析构,而_next是随着n1节点析构而析构
再将n1节点析构,使其引用计数减1,此时n1的引用计数为1,还有一个_prev的智能指针指向n1,只有当_prev析构n1才能析构,而_prev是随着n2节点析构而析构

就造成了循环引用,从而导致内存泄漏


库中为了解决循环引用的问题,所以提出了 weak_ptr(弱指针)

特点:
不是常规的智能指针,不支持RAII(利用对象生命周期来控制程序资源)
支持像指针一样
专门设计出来辅助解决 shared_ptr的循环引用问题


将_next和_prev改为 weak_ptr即可解决问题
使用weak_ptr可以指向资源,但不参与管理,不增加引用计数


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

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

相关文章

RocketMQ教程-(4)-领域模型-消费者(Consumer)

本文介绍 Apache RocketMQ 中消费者&#xff08;Consumer&#xff09;的定义、模型关系、内部属性、行为约束、版本兼容性及使用建议。 定义​ 消费者是 Apache RocketMQ 中用来接收并处理消息的运行实体。 消费者通常被集成在业务系统中&#xff0c;从 Apache RocketMQ 服务…

【JVM】详解对象的创建过程

文章目录 1、创建对像的几种方式1、new关键字2、反射3、clone4、反序列化 2、创建过程步骤 1、检查类是否已经被加载步骤 2、 为对象分配内存空间1、指针碰撞针对指针碰撞线程不安全&#xff0c;有两种方案&#xff1a; 2、空闲列表选择哪种分配方式 步骤3、将内存空间初始化为…

如何在armv6 armv7 armv8(aarch64)嵌入式板子上面安装nginx服务器,支持H265码流

如何在armv6 armv6 armv8 aarch64 嵌入式板子上面安装nginx服务器支持推送H265的视频流 开始吧 一&#xff0c;准备工作二&#xff0c;configure时遇到的出错问题1、checking for C compiler … found but is not working2&#xff0c;error: can not detect int size3&#xf…

【博客682】k8s apiserver bookmarks机制以更高效检测变更

k8s apiserver bookmarks机制以更高效检测变更 list-watch背景&#xff1a; List-Watch 是kubernetes中server和client通信的最核心的机制&#xff0c; 比如说api-server监听etcd&#xff0c; kubelet监听api-server&#xff0c; scheduler监听api-server等等&#xff0c;其实…

Jmeter场景设置与监听

Jmeter场景设置 场景的概念: 场景是用来尽量真实模拟用户操作的工作单元&#xff0c;场景设计源自于用户真实操作。 场景设计: 场景设计是根据收集分析用户的实际操作而定义的Jmeter脚本的执行策略。 性能测试中涉及的基本场景有两种&#xff0c;即单一业务场景和混合业务场景…

链表是否有环、环长度、环起点

问题引入 如何检测一个链表是否有环&#xff0c;如果有&#xff0c;那么如何确定环的长度及起点。 引自博客&#xff1a;上述问题是一个经典问题&#xff0c;经常会在面试中被问到。我之前在杭州一家网络公司的电话面试中就很不巧的问到&#xff0c;当时是第一次遇到那个问题&…

RocketMQ集群4.9.2升级4.9.6版本

本文主要记录生产环境短暂停机升级RocketMQ版本的过程 一、整体思路 1.将生产环境MQ4.9.2集群同步到测试环境&#xff0c;并启动&#xff0c;确保正常运行。 2.参照4.9.2配置4.9.6集群 3.停掉4.9.2集群&#xff0c;启动4.9.6集群&#xff0c;测试确保正常运行。 4.停掉4.9.6集…

JSON格式Python,Java,PHP等封装获取淘宝商品快递费用数据API方法

淘宝是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取淘宝天猫商品详情快递费用数据&#xff0c;您可以通过开放平台的接口或者直接访问淘宝天猫商城的网页来获取商品详情快递费用。以下是两种常用方法的介绍&a…

leetcode132. 分割回文串 II(java)

分割回文串 II 分割回文串 II动态规划 回文字符串 分割回文串 II 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文。 返回符合要求的 最少分割次数 。 示例 1&#xff1a; 输入&#xff1a;s “aab” 输出&#xff1a;1 解释&#xff1a;…

git使用教程

一 创建环境 参考 Git 安装配置 | 菜鸟教程 (runoob.com)https://www.runoob.com/git/git-install-setup.html 1.1 配置 $ git config --global user.name "runoob" $ git config --global user.email test@runoob.com 1.2 创建一个新文件夹 在新的文件夹执行(…

C++中的头文件.h 和 源文件.cpp 的关系

在VS中 C项目&#xff0c;我创建了一个类&#xff0c; 会自动创建头文件和源文件&#xff0c;这两个文件有什么关系&#xff1f; 如何快速切换&#xff1f;在头文件.h文件中声明的类方法&#xff0c; 如何快速在源文件中进行具体实现&#xff1f;在 Visual Studio 中创建 C 项…

多级调度在工程中的应用及思路

多级调度在工程中的应用及思路 集中式多机调度 顾名思义&#xff1a;集中式多机调度就是指&#xff0c;机器的数据是集中的&#xff0c;有统一的控制中心调度&#xff0c;类似于现实中的机场调度中心&#xff0c;铁路调度中心&#xff0c;一般机器将定位&#xff0c;速度&…

文件上传漏洞总结2

文件上传的大体都已经学习过了 这个假期在给他强化一下 什么是webshell webshell是web入侵的脚本攻击工具。webshell就是一个asp或php木马后门&#xff0c;黑客在入侵了一个网站后&#xff0c;常常在将这些asp或php木马后门文件放置在网站服务器的web目录中&#xff0c;与正常…

HTML5的应用现状与发展前景

HTML5&#xff0c;作为Web技术的核心&#xff0c;已经深深地改变了我们看待和使用Web的方式。它不仅提供了数不尽的新特性和功能&#xff0c;还使得Web设计和开发更加互动、更加直观。这篇文章将探讨HTML5的当前应用现状&#xff0c;以及它的未来发展前景。 HTML5的应用现状 H…

HDU-7323 2023“钉耙编程”杭电多校赛(3)a-b Problem

HDU-7323 2023“钉耙编程”杭电多校赛&#xff08;3&#xff09;a-b Problem 题目大意 小 A A A和小 B B B在玩游戏。有 n n n块石头&#xff0c;小 A A A和小 B B B轮流捡&#xff0c;小 A A A先捡。每人每次只能捡一块石头&#xff0c;直到所有的石头都被捡完。 每块石头都…

【linux升级ssh】 利用rpmbuild工具对ssh打包为rpm包进场安装升级

制作rpm包 rpmbuild命令用于创建软件的二进制包和源代码包。 官方文档&#xff1a;rpm.org - RPM Reference Manual rpmbuild 中文手册&#xff1a;rpmbuild 中文手册 [金步国] 使用rpmbuild将tar包打成rpm包 RPM打包使用的是rpmbuild命令&#xff0c;这个命令来自rpm-buil…

二、深入浅出WPF之系统学习XAML语法

二、系统学习XAML语法 2.1 Xaml的树形结构 跟Winforms一样,UI也是个平面结构,与winforms的设计思维不同,WPF使用树形逻辑来描述UI,下面是UI布局的简单代码 <StackPanel Background="LightBlue"><TextBox x:Name="textBox1" Margin="5&q…

本地缓存LoadingCache

引入依赖 <!-- https://mvnrepository.com/artifact/com.google.guava/guava --> <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.1.1-jre</version> </dependency>主要代…

(三)InfluxDB入门(借助Web UI)

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 3 章 InfluxDB入门&#xff08;借助Web UI&#xff09; 借助Web UI&#xff0c;我们可以更好地理解InfluxD…

C语言入门篇(八)

前言   本篇分享的是部分操作符的概念与用法&#xff0c;从经典例题入手&#xff0c;带你快速了解和掌握。   收录专栏&#xff1a;浅谈C语言 操作符详解上 1. 操作符分类2. 算术操作符3. 移位操作符3.1 左移操作符3.2 右移操作符 4. 位操作符5. 赋值操作符6. 单目操作符6.…