智能指针的介绍

news/2024/4/30 9:51:58/文章来源:https://blog.csdn.net/Rinki123456/article/details/127178384

目录

1. 什么是RAII?

2. auto_ptr

3. boost

4. unique_ptr

5. 定制删除器

6. shared_ptr

7. week_ptr


1. 什么是RAII?

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:

  1. 不需要显式地释放资源。
  2. 采用这种方式,对象所需的资源在其生命期内始终保持有效
// 利用RAII思想写出的智能指针模板template <class T>
class SmartPtr
{SmartPtr(T* ptr):_ptr(ptr);{}~SmartPtr(){delete _ptr;} T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
}; 

2. auto_ptr

在C++98中就提供了一种类似于智能指针的指针。

auto_ptr的原理是:将管理权转移的思想

具体实现如下:

#pragma once
namespace bit
{template<class T>class auto_ptr{//构造函数auto_ptr(T* ptr):_ptr(ptr){}//转移资源auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){ap._ptr = nullptr;}//赋值auto_ptr<T>& operator=(auto_ptr<T>& ap){if (this != &ap){if (_ptr){delete _ptr;}_ptr = ap._ptr;ap._ptr = nullptr;}return *this;}//析构函数~auto_ptr(){if (_ptr){delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return  _ptr;}private:T* _ptr;};
}

这样有一种缺陷就是赋值的指针将会变成空指针,这种效果也遭受了很多学习C++语法人的吐槽,以至于很多公司禁止使用该指针。

这种“智能指针”不能满足我们的要求,因此在C++11出现之前,广泛的C++使用者就聚齐在一起,形成了一个社区:boost

3. boost

什么是boost?

 而我们的智能指针就出自于此,较为著名的智能指针包括:scoped_ptr 、shared_ptr 、week_ptr

 而C++出台的unique_ptr、shared_ptr、week_ptr都出自于此。

4. unique_ptr

什么是unique_ptr?

通过名字我们可以发现它被称为独一无二的指针,也就是说明它是无法被复制的,而它的底层也确实如此。

 通过它的拷贝构造,我们可以发现它没有对象构造和赋值构造,这也就保证了该指针指向的空间只有它一个指向。

那么我们来观察它的底层是如何实现的:

using namespace std;
namespace bit
{template<class T>struct default_delete{void operator()(T* ptr){delete ptr;ptr = nullptr;}};template<class T,class D = default_delete<T>>class unique_ptr{public://构造函数unique_ptr(T* ptr):_ptr(ptr){}unique_ptr(unique_ptr<T>& ap) = delete;//赋值unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;//析构函数~unique_ptr(){if (_ptr){cout << "delete ptr" << endl;D d;d(_ptr);}}T& operator*(){return *_ptr;}T* operator->(){return  _ptr;}private:T* _ptr;};
}

在它的底层中将其中两个构造函数加上了delete,在C++11中新增加了delete和default关键字,如果一个函数后加上delete,表示无法默认生成。那么就表明如果不显式写这两个构造函数,那么就能够实现unique这个特点。

至于这里还存在一个叫D的类型,它表示我们存储的指针以什么形式释放

这里我们就称为定制删除器

5. 定制删除器

如果我们传入的值是通过new实现出来的,那么我们在释放的时候就需要通过delete来进行释放

如果是new [],那么我们就需要delete[]来进行释放,所有我们的关键点就是如何在析构的时候释放空间,这里我们就涉及到了定制删除器。

定制删除器顾名思义就是对不同的步骤定制了不同的解决方案,

比如如果传入的值是通过new来实现的,那么就需要使用delete

如果是通过malloc来实现的,那么就需要使用free

比如unique_ptr,我们就可以发现它不仅仅是传入一个T参数,还需要传入一个D参数,这个D参数就是我们的定制删除器,就可以在析构中调用D所形成的对象来释放我们的指针。

6. shared_ptr

 有一种情况:多个指针管理同一块区域,那么我们这块区间是什么时候进行释放?

这里我们就用到了shared_ptr,那么shared_ptr是如何来实现区间的释放呢?

其实是采用了计数的原理,就好比老师让最后一个离开教室的把们关上一样,如果最后只剩下一个指针管理这块区域,那么当对象销毁时,这块空间也随之释放。

shared_ptr的特点:

1. 通过计数来实现多个shared_ptr对象管理同一块资源

2. 当对象被销毁时,将每个资源所指向的计数-1,

3. 如果当前资源的计数为0,则将其释放,如果不为0,则说明还有指针指向当前资源

那么它的基本实现是怎样呢?

#pragma once
namespace bit
{template<class T>class shared_ptr{void Release(){if (--(*_count) && _ptr){delete _ptr;_ptr = nullptr;delete _count;_count = nullptr;}}//构造函数shared_ptr(T* ptr):_ptr(ptr),_count(new int(1)){}//转移资源shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr),_count(sp._count){++(*_count);}//赋值shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_count = sp._count;*(_count)++;}return *this;}//析构函数~shared_ptr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return  _ptr;}T* get(){return _ptr;}private:T* _ptr;int* _count;};
}

这里最为关键的就是Release函数,通过该函数我们可以得知是否所指向的资源要释放,这也是shared_ptr的灵魂所在。

7. week_ptr

那么shared_ptr就是完美了吗?

在不整花活的情况下,使用shared_ptr是完全足够的,但是总会有特殊情况

 如果我们要实现这样的情况,会发生什么呢?

我们发现在程序终止时,没有打印出析构函数里面的字符串,因为我们可以发现没有当p1、p2出了作用域后,没有进行销毁,产生了内存泄漏。

那么这是怎么发生了呢?

 

 

 那么当p1、p2释放后,计数值还是1,_next的释放取决于_prev释放,而_prev的释放又取决于_next,那么这样相互依赖的关系被称为循环引用。

那么我们是如何来避免进行循环引用的呢?,这里C++11就提出了一个解决方案:引入了一个新指针-- week_ptr,由于我们的计数的由于_next和_prev指向了空间,导致我们的计数+1,又因为我们想让_next和_prev指向空间,因为我们可以当它指向空间时。当前空间不计数,这也是week_ptr的原理,也就是小弟帮大哥办事。

namespace bit
{template <class T>class week_ptr{public:week_ptr(const T* ptr):_ptr(ptr){}week_ptr(const week_ptr<T>& wp):_ptr(wp._ptr){}week_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}void operator = (const shared_ptr<T>& sp){_ptr = sp.get();}private:T* _ptr;};
}

只干事没有实权,这就是小弟应该做的事!!!

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

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

相关文章

torch张量的维度有关的变换——注意:对张量采取维度变换的时候一定要注意要追踪每个维度的数据,否则会出错

文章目录重塑维度 reshape / view增加 / 减少维度 unsqueeze / squeeze交换维度 transpose / permute将维度扩充 Expand / repeat / broadcasttranspose & view 的连续变化张量比较使用 torch.all() & torch.eq()重塑维度 reshape / view 增加 / 减少维度 unsqueeze /…

[ccc3.0][数字钥匙] UWB MAC协议(一)

UWB MAC协议--jianqiang.xue一、测距交换序列&#xff08;一&#xff09;Pre-POLL请求消息及其参数&#xff08;二&#xff09;Final_Data 消息及其参数一、测距交换序列 本节详细介绍了DK MAC协议&#xff0c;用于在响应者设备的发起者和每个响应者之间进行三包交换的双向测距…

Redis概述

目录 一、NoSQL数据库 1.技术发展 2.NoSQL数据库 ①session问题 ②解决IO压力 ③NoSQL概述 ④NoSQL适用场景 ⑤NoSQL不适用的场景 ⑥NoSQL的分类 ⑦NoSQL的特点 ⑧Memcache ⑨Redis ⑩ MongoDB 官网地址&#xff1a;Redis 一、NoSQL数据库 NoSQL_百度百科 (baidu…

保护PDF文件的3种方法

做好的PDF文件我们可以根据不同需求来设置保护。 方法一 如果希望PDF文件不能被随意打开&#xff0c;我们可以设置打开密码&#xff0c;这样点击PDF文件后就会提示需要输密码才能进一步打开。 如何设置PDF打开密码呢&#xff1f;一般PDF编辑器都可以设置&#xff0c;如果没有…

SpringBoot Web开发设置请求映射规则

前言 本篇文章介绍 SpringBoot Web 开发中的请求映射注解的详细使用&#xff0c;希望观众老爷们多多支持&#xff0c;欢迎在评论区批评指正&#xff01; 设置请求映射规则通过 RequestMapping注解&#xff0c;该注解标注在类或者方法上&#xff1b; 该注解用来设定所能匹配请…

顺序查找和二分查找

案例1):1 #include <stdio.h>2 3 int seqSearch(int arr[], int arrLen, int val){ //定义一个数组,一个数组长度,目标值4 for (int i = 0; i < arrLen; i++){5 if(arr[i] == val){6 return i;7 }8 }9 return -1; //找…

Java8 函数式编程

文章目录Java 函数式编程1. Lambda 表达式1.1 标准格式1.2 使用前提1.2.1 一个参数1.2.2 多个参数1.2.3 有返回值1.3 省略简化1.4 函数式接口1.4.1 Supplier1.4.2 Consumer1.4.3 Predicate1.4.4 Function1.5 方法引用1.5.1 对象 :: 实例方法1.5.2 类 :: 静态方法1.5.3 类 :: 实…

期货价格怎么算出来的?

期货价格怎么算出来的&#xff1f; 期货价格现货价格融资成本 如果对应资产是一个支付现金股息的股票组合&#xff0c;那么购买期货合约的一方因没有马上持有这个股票组合而没有收到股息。相反&#xff0c;合约卖方因持有对应股票组合收到了股息&#xff0c;因而减少了其持仓成…

数据结构-泛型(Java)

文章目录一、什么是泛型&#xff1f;1、非泛型2、泛型3、泛型的使用 泛型类 泛型接口 泛型方法二、泛型类1、 泛型类 正确使用分析 错误使用分析2、泛型类实现抽奖器3、泛型类派生子类 泛型类派生子类第一种第二种 非泛型三、泛型接口第一种&#xff1a;泛型类实现泛型接口第二…

使用python的pygame做的小游戏项目:小船打鱼

python小游戏项目&#xff1a;小船打鱼成果展示代码解析go_fishing.pygame_function.pygame_stats.pyscoreboard.pyalien.pysettings.pyship.pybullet.pybutton.py存在的问题代码都在这里&#xff0c;只需要创建好项目&#xff0c;将对应的代码保存在对应文件名的文件中即可&am…

【微搭低代码】Javascript基础知识-函数及模块介绍

低代码要想入门&#xff0c;首先需要学习javascript&#xff0c;我们已经有了两篇基础文章 变量定义及初始化 循环及条件控制 我们本篇介绍两个知识点&#xff0c;一个是函数&#xff0c;一个是模块 函数 在js中函数是可以重复使用的代码块&#xff0c;定义函数是为了去除冗余…

在Windows下自制ARM交叉编译工具链

参考链接&#xff1a;gnu工具链 1.Download MinGW and MSys packages. 安装MSys 参考此链接https://www.msys2.org/安装&#xff0c;注意只需要安装即可。 安装开发环境,设置镜像,需要进入安装路径中的/etc/pacman.d/进行修改 // /etc/pacman.d/mirrorlist.mingw32 Serve…

【5G RRC】5G 切换(handover)那点事儿

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

python去图片背景

Remove Image Background using Python https://youtu.be/RkdFkhfMK2k

跨境电商必读,WhatsApp营销入门指南!

关键词&#xff1a;WhatsApp营销、跨境电商营销 现在&#xff0c;跨境社交媒体和Messengers不仅仅是私人交流的渠道了。很多跨境电商已经找到了在WhatsApp营销的秘诀&#xff0c;如果你还没开始&#xff0c;你可能已经落后了。同时&#xff0c;与其他平台相比&#xff0c;在 W…

Vue组件-卡片动画倒计时

前言 最近有朋友在做投票的项目&#xff0c;里面有用到一个倒计时的组件&#xff0c;还想要个动画效果。cv大法浸染多年的我&#xff0c;首先想到的是直接找个现有的组件。 通过一通搜索&#xff0c;看上的只有一个 vue2-flip-countdown&#xff0c;但是当我要修改大小和颜色…

(附源码)计算机毕业设计SSM游乐园娱乐项目管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Github的使用教程

文章目录注册查找仓库下载代码fork仓库管理创建仓库添加文件提交issue提交/接受PRpages一直想进入工程这块领地&#xff0c;但是好像没咋学过github&#xff0c;今天学一下&#xff0c;先上个名词解释 注册 首先&#xff0c;github其实是不需要邮箱和手机号的&#xff0c;可以…

window11下安装.framework3.5的方法

window11下安装.framework3.5的方法 如果正常安装报错了&#xff0c;可采用如下方法重新安装 一、把安装iso文件 zh-cn_windows_11_business_editions_version_22h2_updated_sep_2022_x64_dvd_515a832b.iso 装载到虚拟盘中H:\sources\sxs\中的文件拷贝到硬盘已存在的盘符F:\w…

容器适配器——stack/queue/priority_queue

目录 一. stack 二. queue 三. priority_queue 1. empty()&#xff0c;top()&#xff0c;size()的实现 2. pop和push的实现 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结)&#xff0c;该种模式是将一个类的接口转…