【1++的C++进阶】之智能指针
👍作者主页:进击的1++
🤩 专栏链接:【1++的C++进阶】
文章目录
- 一,什么是智能指针
- 二,为什么需要智能指针
- 三,智能指针的发展
一,什么是智能指针
要了解智能指针,我们先要了解RAII.
RAII是一种利用对象生命周期来控制资源的技术。
在对象初始化时,其接管资源,在对象的生命周期内其管理的资源始终保持有效,最后当对象析构时,释放资源。
那么什么是智能指针呢?
智能指针就是利用了RALL的原理,并且通过封装,使得它的对象能够向指针一样使用。因此其重载了*,->。
下面是一个简单的智能指针代码:
template<class T>class smart_ptr{public:smart_ptr(T* ptr):_ptr(ptr){}~smart_ptr(){if (_ptr){delete _ptr;}}T& operator*(){return *_ptr;}T* operator ->(){return &_ptr;}private:T* _ptr;};
二,为什么需要智能指针
我们来看一段代码:
double divide(int a, int b)
{if (b == 0)throw "除0错误";elsereturn a / b;
}
void func()
{int* arr = new int[10];//申请空间try{int a, b;cin >> a >> b;divide(a, b);}catch (...){//在这里进行校对后,再抛出delete[]arr;cout << "delete[]arr" << endl;throw;}delete []arr;//若出现异常,则不会执行到这里,会造成内存泄漏。cout << "delete[]arr" << endl;}
int main()
{try{func();}catch (const char* errmsg){cout << errmsg << endl;}catch (...){cout << "错误" << endl;}return 0;
}
上述代码中,我们为了防止内存泄漏,因此进行了校正,再抛出的操作,这样做相对来说比较麻烦,而且我们在写代码时也是容易忘记校正的操作,这时就需要我们的智能指针了。我们在申请资源的函数内部实例化除一个智能指针的对象,将资源交给它管理,当这个函数调用完成退出后,这个对象也会进行析构,其管理的资源也就释放了。是不是相当方便。
上述提到了内存泄漏,接下来我们就专门来谈一谈内存泄漏。
什么是内存泄漏呢?
内存泄漏是指由于我们的失误,未能释放我们已经不适用的内存,而造成资源的浪费。也可以说是,我们由于设计错误,而对该段内存失去了控制权,进而造成了空间的浪费。
内存泄漏分类:
- 堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak - 系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
三,智能指针的发展
在我们前面写的智能指针中我们会发现其不能够拷贝构造和赋值。因为若我们用默认生成的拷贝构造或赋值的话,由于两个指针指向同一块空间,那么这块空间在释放时就会被释放两次而导致错误。
因此在C++98中就有了auto_ptr的出现,它在面对拷贝构造和赋值问题的解决办法是:管理权的转移-----也就是:当通过A构造出或将A赋值给B时,A的管理权转让给B,A不再进行管理。
这样的设计在后来经常为人所诟病。因为其在拷贝或赋值后,原来的对象指向的空间会被置空,也就是失去了管理权。
下面是其实现代码:
template<class T>class auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptr& s){_ptr = s._ptr;s._ptr = nullptr;}auto_ptr& operator=(auto_ptr s){if (s._ptr != this->_ptr){delete _ptr;_ptr = s._ptr;s._ptr = nullptr;}return *this;}~auto_ptr(){if (_ptr){delete _ptr;}}T& operator*(){return *_ptr;}T* operator ->(){return &_ptr;}T* get(){return _ptr;}private:T* _ptr;};
由于auto_ptrd的缺陷,在C++11中出现了unique_ptr。
unique_ptr的的原理非常粗暴----既然拷贝和赋值是个坑,那我直接禁用你。。。
因此在unique_ptr中,其主要改变就是禁用了拷贝和赋值。
禁用拷贝和赋值有下面几种方法:
- 成员函数私有化。
- 只声明不定义
- 用delete关键字修饰。
下面是unique_ptr的实现:
template<class T>class unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}unique_ptr(unique_ptr& s) = delete;unique_ptr<T>& operator=(unique_ptr s) = delete;~unique_ptr(){if (_ptr){delete _ptr;}}T& operator*(){return *_ptr;}T* operator ->(){return &_ptr;}T* get(){return _ptr;}private:T* _ptr;};
但是,在有些场景下两个对象管理同一块空间的这种需求还是有的,因此C++11中还出现了更加靠谱的shared_ptr。
其原理就是增加了引用计数。对于同一块资源,每赋值或拷贝一次,计数就加1 。每析构一个对象就减一。直到为0也就是只有一个对象在维护着这块空间时,其就释放这块资源。
下面是shared_ptr的实现:
template<class T>class shared_ptr{public:shared_ptr(T* ptr):_ptr(ptr), count_ptr(new int(1)){}shared_ptr(const shared_ptr& s):_ptr(s._ptr){(*s.count_ptr)++;count_ptr = s.count_ptr;}shared_ptr<T>& operator=(const shared_ptr& s){if (_ptr != s._ptr){_ptr = s._ptr;(*s.count_ptr)++;count_ptr = s.count_ptr;}return *this;}~shared_ptr(){if (*count_ptr > 1){(*count_ptr)--;}else{delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get()const{return _ptr;}private:T* _ptr;int* count_ptr;};
但是,shared_ptr也是有一些问题的。
- 线程安全问题----我们在后面的文章会进行讲解。
- 循环引用问题和对象的删除问题
接下来我们讲 2 中的两个问题。
循环引用问题:
我们先来看看什么是循环引用问题:
如图,就是我们的循环引用,其到底会产生什么问题呢?
我们慢慢往下看。
当我们node1,node2析构后,其计数都为1,还并没有释放掉。只有_next释放掉,node2才能释放,_prev释放掉,node1才能释放。而只有node1释放掉_next才能释放,node2释放,_prev才能释放。
这就形成了一个死循环。谁的释放不了。
因此引入了weak_ptr。
其就是用来解决循环引用问题的。
其解决循环引用的原理就是:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr类型。node1->_next = node2;和node2->_prev = node1;时weak_ptr的_next和_prev不会增加node1和node2的引用计数。
这样在析构node1和node2时,其管理的资源也都进行了释放。
下面是weak_ptr的实现代码:
template<class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& s):_ptr(s.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& s){_ptr = s.get();return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
我们再来解决删除的问题:
我们在《内存管理》这篇文章中讲过new 和delete要搭配使用,new …[n]要与delete [] …,malloc与free搭配…
并且我们讲了new和malloc的区别,delete与free的区别。
那么new 与new[] ,delete与delete[]有什么区别呢?
下面我们来用一张图讲解清楚:
可见delete与delete[]区别在于调用析构函数的次数和释放空间的指针的位置。因此,当我们用delete去释放一个本该用delete[]释放的对象时,便会发生错误。
我们也不能去修改库里的析构函数。那我们该怎么解决呢?
C++11中给我们提供了在构造智能指针对象时可以传一个删除器的对象来应对不同方式申请的对象。
下面我们用仿函数的形式进行模拟:
代买实现如下:
template<class T,class D>class shared_ptr{public:shared_ptr(T* ptr):_ptr(ptr), count_ptr(new int(1)){cout << "构造" << endl;}shared_ptr(const shared_ptr& s):_ptr(s._ptr){(*s.count_ptr)++;count_ptr = s.count_ptr;cout << "拷贝构造" << endl;}shared_ptr<T,D>& operator=(const shared_ptr& s){if (_ptr != s._ptr){_ptr = s._ptr;(*s.count_ptr)++;count_ptr = s.count_ptr;cout << "赋值" << endl;}return *this;}~shared_ptr(){if (*count_ptr > 1){(*count_ptr)--;cout << "count--" << endl;}else{D()(_ptr);//cout << "delete" << endl;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get()const{return _ptr;}private:T* _ptr;int* count_ptr;};删除器template<class T>class Free{public:void operator()(T* ptr){cout << "Free" << endl;free(ptr);}};template<class T>class Delete{public:void operator()(T* ptr){cout << "Delete" << endl;delete ptr;}};template<class T>class Delete_{public:void operator()(T* ptr){cout << "Delete_" << endl;delete [] ptr;}};
相关文章:
【1++的C++进阶】之智能指针
👍作者主页:进击的1 🤩 专栏链接:【1的C进阶】 文章目录 一,什么是智能指针二,为什么需要智能指针三,智能指针的发展 一,什么是智能指针 要了解智能指针,我们先要了解RA…...
一百七十九、Linux——Linux报错No package epel-release available
一、目的 在Linux中配置Xmanager服务时,执行脚本时Linux报错No package epel-release available 二、解决措施 (一)第一步,# wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm (二&…...
【AI视野·今日CV 计算机视觉论文速览 第248期】Mon, 18 Sep 2023
AI视野今日CS.CV 计算机视觉论文速览 Mon, 18 Sep 2023 Totally 83 papers 👉上期速览✈更多精彩请移步主页 Interesting: 📚Robust e-NeRF,处理高速且大噪声事件相机流的NERF模型。(from NUS新加坡国立) 稀疏噪声事件与稠密事件数据的区别:…...
解决Vue项目中的“Cannot find module ‘vue-template-compiler‘”错误
1. 问题描述 在Vue项目中,当我们使用Vue的单文件组件(.vue文件)时,有时会遇到以下错误信息: ERROR: Cannot find module vue-template-compiler这个错误通常发生在我们使用Vue的版本不匹配或者缺少必要的依赖模块时。…...
tensorflow基础
windows安装tensorflow anaconda或者pip安装tensorflow,tensorflow只支持win7 64系统,本人使用tensorflow1.5版本(pip install tensorflow1.5) tensorboard tensorboard只支持chrome浏览器,而且加载过程中可能有一段…...
spring_注解笔记
spring使用注解开发 文章目录 1.前提1 Bean2 属性注入3 衍生的注解4.自动装配5 作用域 1.前提 步骤1: 要使用注解开发,就必须要保证AOP包的导入 步骤2: xml文件添加context约束 步骤3: 配置注解的支持 <context:annotation-…...
c++运算符重载
目录 运算符重载的基本概念 重载加号运算符() 类内实现 类外实现 运算符重载碰上友元函数 可重载和不可重载的运算符 可重载的运算符 不可重载的运算符 重载自加自减运算符(a a) 智能指针 重载等号运算符() 重载等于和不等运算符(…...
vue子组件向父组件传参的方式
在Vue中,子组件向父组件传递参数可以通过自定义事件和props属性来实现。下面是一些关键代码示例: 1. 使用自定义事件: 在子组件中,通过 $emit 方法触发一个自定义事件,并传递参数。 <template><button cli…...
代码随想录Day41| 343. 整数拆分 |
343. 整数拆分 class Solution { public:int integerBreak(int n) {vector<int> f(n1,0);f[2]1;for(int i3;i<n;i){for(int j1;j<i-1;j){f[i]max(f[i],max(f[i-j]*j,(i-j)*j));}}return f[n];} }; 96. 不同的二叉搜索树 class Solution { public:int numTrees(int…...
工厂模式-(简单工厂模式)
首先看一下设计模式的六大原则 设计模式的六大原则 1、开闭原则(Open Close Principle) 开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概…...
V8引擎是如何提升对象属性访问速度的?
JavaScript 中的对象是由一组组属性和值的集合,从 JavaScript 语言的角度来看,JavaScript 对象像一个字典,字符串作为键名,任意对象可以作为键值,可以通过键名读写键值。 然而在 V8 实现对象存储时,并没有…...
彩色相机工作原理——bayer格式理解
早期,图像传感器只能记录光的强弱,无法记录光的颜色,所以只能拍摄黑白照片。 1974年,拜尔提出了bayer阵列,发明了bayer格式图片。不同于高成本的三个图像传感器方案,拜尔提出只用一个图像传感器,在其前面放…...
IDEA中DEBUG技巧
Debug 介绍 Debug 设置 如上图标注 1 所示,表示设置 Debug 连接方式,默认是 Socket。Shared memory 是 Windows 特有的一个属性,一般在 Windows 系统下建议使用此设置,相对于 Socket 会快点。 ## Debug 常用快捷键 Win 快捷键M…...
人工智能训练师
人工智能训练师是一个较新的职业,2020年2月才被正式纳入国家职业分类目录。他们主要负责在人工智能产品使用过程中进行数据库管理、算法参数设置、人机交互设计、性能测试跟踪及其他辅助作业。 这个职业的背景源于AI公司从客户(用户)那里获取…...
【业务功能118】微服务-springcloud-springboot-Kubernetes集群-k8s集群-KubeSphere-OpenELB部署及应用
OpenELB部署及应用 一、OpenELB介绍 网址: openelb.io OpenELB 是一个开源的云原生负载均衡器实现,可以在基于裸金属服务器、边缘以及虚拟化的 Kubernetes 环境中使用 LoadBalancer 类型的 Service 对外暴露服务。OpenELB 项目最初由 KubeSphere 社区发…...
Unity中Shader的模板测试
文章目录 前言什么是模板测试1、模板缓冲区2、模板缓冲区中存储的值3、模板测试是什么(看完以下流程就能知道模板测试是什么)模板测试就是在渲染,后渲染的物体前,与渲染前的模板缓冲区的值进行比较,选出符合条件的部分…...
Scala 高阶:Scala中的模式匹配
一、概述 Scala中的模式匹配(case)类似于Java中的switch...case,但是Scala的模式匹配功能更为强大。通过模式匹配,可以匹配更复杂的条件和数据结构,包括常量、类型、集合、元组等。而 Java 的 switch 语句只能用于匹配…...
分子生物学——分子机器
分子生物学——分子机器 文章目录 前言一、2016年度诺贝尔化学奖1.1. 介绍1.2. 什么是分子机器?1.3. 分子机器的意义 总结 前言 对于本次搜集分子生物学领域的一个诺贝尔奖的有关内容的作业 参考文献: https://www.cas.cn/zt/sszt/2016nobelprize/hxj/2…...
【简历优化】这套「实习、初级、中级」测试工程师求职简历模板,建议收藏。
历时2年,7000粉丝问答,帮助上百位“刚培训毕业”、“1~3年经验”的软件测试伙伴,成功入职! 我将这些问题内容,会持续更新记录在 「软件测试」求职指南 专栏。 求职简历中的误区 对于简历应该具备哪些模块,…...
vue中展示json数据的方法
推荐插件:bin-code-editor (gitee.io) bug-1:编辑器无法显示数据 原因:组件层级套用太深,导致无法显示数据 解决办法:减少在孙子及后代组件中使用插件。...
【SG滤波】三阶滤波、五阶滤波、七阶滤波(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
2013 ~【VUE+ ElementUI】——【上传、下载】进度计算
【VUE ElementUI】——【上传、下载】进度计算 上传:FormData方式上传,监听 onUploadProgress下载:blob文件流下载,监听 onDownloadProgress 上传:FormData方式上传,监听 onUploadProgress <el-upload:…...
android可见即可说实现方案
依赖于科大讯飞的asr识别能力,使用Android无障碍服务获取页面文本作为热词,注册到讯飞api,注册过后语音识别到热词的asr返回,利用WindowManager和无障碍的点击实现可见即可说功能 ##  无障碍服务获取需要注册的热词package com..mo…...
Pikachu Burte Force(暴力破解)
一、Burte Force(暴力破解)概述 “暴力破解”是一攻击具手段,在web攻击中,一般会使用这种手段对应用系统的认证信息进行获取。 其过程就是使用大量的认证信息在认证接口进行尝试登录,直到得到正确的结果。 为了提高…...
SpringMVC之JSON返回及异常处理
目录 JSON处理 导入依赖 配置Spring-mvc.xml ResponseBody注解使用 测试 目录 JSON处理 导入依赖 配置Spring-mvc.xml ResponseBody注解使用 测试 Jackson 定义 用法 常用注解 统一异常处理 为什么要全局异常处理? 异常处理思路 SpringMVC异常分类 综…...
SkyWalking快速上手(六)——告警
文章目录 前言一、什么是SkyWalking的告警功能二、为什么要使用SkyWalking的告警功能1. 及时发现异常情况2. 提高故障处理效率3. 避免数据丢失和损坏4. 提升系统性能和稳定性 三、如何使用SkyWalking的告警功能1. 告警规则2. 告警通知3. 告警持续时间 四、注意事项1、合理设置告…...
docker run:--privileged=true选项解析(特权模式:赋予容器几乎与主机相同的权限)
文章目录 Docker的--privilegedtrue选项1. Docker 容器的安全性1.1 Linux Namespace 和 Capabilities1.2 限制和权限 2. Docker的--privilegedtrue选项2.1 --privilegedtrue的作用2.2 --privilegedtrue的风险 3. 结论 Docker的–privilegedtrue选项 Docker在创建和运行容器时&…...
计算机专业毕业设计项目推荐06-工作室管理系统(Java+Vue+Mysql)
工作室管理系统(JavaSpringVueMysql) **介绍****系统总体开发情况-功能模块****各部分模块实现****最后想说的****联系方式** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较…...
Python 文件的读写操作
文章目录 1. 文件对象1.1 文件打开方式1.1.1 打开文件1.1.2 关闭文件1.1.3 访问模式 1.2文件读取1.2.1 read()1.2.2 readline()1.2.3 readlines() 1.3 文件迭代1.4 文件输入1.4.1 write()1.4.2 writelines() 1. 文件对象 文件读写操作: 把大象放冰箱里,…...
多线程回顾、集合Collection、Set、List等基本知识
多线程回顾 问: 多线程的两种创建方式? 继承Thread类实现Runnable接口线程池Callable 问:多线程通常会遇到线程安全问题? 什么情况下会遇到线程安全问题? 答:一个数据被多个线程访问(有读有写) 解决这个问题的方式? SE:同步锁 synchronized A : 同步代码块 B : 同步方法…...
wordpress 无权限/培训网站设计
bugly在错误跟踪上功不可没,但是如果在工程中没合理设置也会带来一些令人头疼的问题。比如把调试模式下的错误也上传到bugly,调试时上传mapping文件影响编译速度。这里我提供一种方法解决以上问题: 一. 让bugly仅在打release包时上传mapping…...
成都设计公司展厅设计/深圳网站优化网站
你有遇到这种情况吗?有些网站明明好的。你怎么也打不开。换台电脑就可以。就在你这台电脑上怎么也打不开。我前几天遇到一棘手的事。突然我需要的网站就打不开了。别的网页又好好的1.尝试切换浏览器浏览模式失败。2.删除cooking还是不行。按说就可以了。怎么就不行呢…...
苏州做网站推广的公司哪家好/百度客服电话24小时客服电话
json是什么? 来个形式点的定义 json就是由 { } , [ ] ," " , : 构成的一串字符; 其中: {}表示对象; []表示数组; ""表示属性或值; :表示前面是属性,后面是值 举例如下: ①对象json:{"name":"tom"} ②数组json:["lily",&…...
软件开发和网站建设/微信平台推广方法
命令简介: 该命令用来识别文件类型,也可用来辨别一些文件的编码格式。它是通过查看文件的头部信息来获取文件类型,而不是像Windows通过扩展名来确定文件类型的。 执行权限 :All User 指令所在路径:/usr/bin/file 命令语…...
网站获取用户/google代理
在其他语言中为了避免类和方法重名问题,都有一个类似命名空间的概念,在js中实现类似的功能吗? 可以实现,主要是借助于js中对象的概念来实现,例如: 1 在命名空间中定义方法属性 var GiantCorp GiantCorp||{…...
阿里云网站建设方案书/杭州网站推广公司
本文给大家讲解了关于linux服务器被黑的解决方法。其中的讲到了“root kits”或者流行的刺探工具占用了你的CPU,存储器,数据和带宽的问题。 平时会有一些朋友遇见服务器被黑的问题,经过搜集和整理相关的相关的材料,在这里本人给大…...