智能指针原理、使用和实现——C++11新特性(三)

目录

一、智能指针的理解

二、智能指针的类型

三、shared_ptr的原理

1.引用计数

2.循环引用问题

3.weak_ptr处理逻辑

四、shared_ptr的实现

五、定制删除器

六、源码


一、智能指针的理解

问题:什么是智能指针?为什么要有智能指针?智能指针解决什么样的问题?

要明白上面这些问题,我们先引入一个程序。

double div(double x, double y)
{int* p = new int(120);if (y == 0){string s = "y can not be zero.";throw s;}delete p;return x / y;
}
int main()
{try{double x = 2, y = 0;div(x, y);}catch (string s){cout << s << endl;}return 0;
}

上面这段程序,如果出现抛异常的话,p的内存是没有办法释放的。 

        智能指针简称RAll,是一种自动化管理资源的类模板,这里指的资源可以是:动态开辟的内存,文件指针,网络连接,互斥锁等等。RAII在获取资源时把资源委托给⼀个对象,接着控制对资源的访问,利用对象的⽣命周期结束时会自动析构的特性来完成对资源的自动释放,这样保障了资源的正常释放,避免资源泄漏问题。

二、智能指针的类型

       智能指针其实就是在原指针的基础上套一个类,如果就这样简单的想会有一个非常致命问题——当一块空间被两个智能指针对象指向时会析构两次,从而引发编译器报错

        库中的智能指针有很多,用法都一样,区别在于处理同一块空间会被释放两次的方法不同。接下来我们就来看这些智能指针是如何处理的。

  • auto_ptr:在做拷贝构造和赋值重载时,把原对象的资源管理权直接转移给新的对象,原对象指向空,保证一块资源只被一个智能指针管理,从而达到一块资源只被释放一次的目的。但是这种做法会使原指针悬空,如果被误用会导致访问报错的问题。推荐指数:⭐
  • unique_ptr:不允许做拷贝构造和赋值重载操作,从而达到一块资源只被释放一次的目的。如果用不到这两个函数的话可以考虑用这个智能指针。推荐指数:⭐⭐⭐⭐
  • shared_ptr:支持多个对象指向同一块资源,它底层用的是引用计数的方法,这是一个很优秀的智能指针,在下文我们会重点来学习该指针的具体原理和实现。推荐指数:⭐⭐⭐⭐⭐

三、shared_ptr的原理

1.引用计数

        原理:使用一个变量记录有几个对象在管理该资源,从而决定是否释放该资源。例如:当一个对象走到析构函数时也就是该对象生命结束了不再管理这块资源了,所以引用计数减一,此时如果引用计数变成0的话说明该资源没有对象管理了,可以直接释放,如果不是0,说明还有对象在管理,所以不用处理。

2.循环引用问题

        例如这样一个环形链表,当n1生命周期结束时发现,还有对象(n2的_next)在指向它,所以没有释放资源,当n2生命周期结束时发现还有对象(n1的_next)指向它,所以也没有释放资源。

        也可以这样想:n1的资源什么时候释放,因为n2还在用呢,需要n2的资源释放掉,那么n2的资源什么时候释放,因为n1还在用呢,需要n1的资源释放掉。循环往复始终释放不了,所以导致内存泄漏。

3.weak_ptr处理逻辑

        shared_ptr虽然很优秀,但依旧有缺陷——循环引用问题,所以就有了weak_ptr用来辅助shared_ptr来解决这个问题。

        weak_ptr不⽀持RAII,也不⽀持访问资源,所以我们看⽂档发现weak_ptr构造时不⽀持绑定到资源,只⽀持绑定shared_ptr,绑定到shared_ptr时,不增加shared_ptr的引⽤计数,那么就可以解决上述的循环引⽤问题。

        weak_ptr没有重载operator*和operator->等,因为他不参与资源管理,那么如果他绑定的shared_ptr已经释放了资源,那么他去访问资源就是很危险的。weak_ptr⽀持expired检查指向的资源是否过期,use_count也可获取shared_ptr的引⽤计数,weak_ptr想访问资源时,可以调⽤lock返回⼀个管理资源的shared_ptr,如果资源已经被释放,返回的shared_ptr是⼀个空对象,如果资源没有释放,则通过返回的shared_ptr访问资源是安全的。

如下示例:

#include<iostream>
#include<memory>
using namespace std;
int main()
{shared_ptr<string> p(new string("left"));weak_ptr<string> pw(p);cout << pw.expired() << endl;cout << pw.use_count() << endl;auto p3 = pw.lock();cout << *p3 << endl;return 0;
}

四、shared_ptr的实现

计数器我们可以储存一个int*类型的指针来实现,然后需要一个指针来储存资源的地址。如下:

        构造函数:我们是要用_ptr储存资源的地址空间所以需要传入一个指针,同时需要给_count开辟空间,并且设为1。

My_share_ptr(T* ptr):_ptr(ptr), _count(new int(1)){}

        拷贝构造:只需要进行资源地址的拷贝和引用计数的拷贝,然后将_count++,表示该资源的管理者增加一个。

My_share_ptr(const My_share_ptr<T>& sptr)
{_ptr = sptr._ptr;_count = sptr._count;(*_count)++;
}//也可以用初始化列表的方式来写

拷贝赋值:这个比较复杂它考虑到的因素有很多。

  1. 自己给自己拷贝赋值:对于这种情况,直接做一个特判后直接退出函数,不用做任何操作。
  2. 对于=的左操作对象:在被赋值前先处理一下现在手上的资源,即让_count--然后判断_count是否为0,如果是则进行资源的释放,如果不是则不用做任何处理。
  3. 进行赋值。
  4. 对于=的右操作对象:该对象被拷贝了一份说明对应资源的管理者增加,需要_count++。
My_share_ptr<T>& operator=(const My_share_ptr<T>& sptr)
{if (sptr._ptr == _ptr) return *this;if (--(*_count) == 0){delete _ptr;delete _count;cout << "Delete1()" << endl;}_ptr = sptr._ptr;_count = sptr._count;(*_count)++;return *this;
}

对于其他运算符的重载比较简单,就不再多讲,我在下面的源码给出。

五、定制删除器

        在上面我们用的都是delete来释放资源,但是不够灵活,不同的资源用不同的释放方式,比如数组用delete[ ],文件资源需要用fclose来释放,否则就会出问题。所以就有了定制删除器,可以通过传入仿函数的方式指定资源的释放方式

        删除器如果通过模板参数来实例化的话会显得十分别扭。我们期望使用传参的方式传入删除器。但是又面临一个问题,我们并不知道这个仿函数是什么类型,该怎么储存呢?因为删除器是做资源释放,所以可以确定它的返回类型是void,参数是T*,那么此时此刻包装器就起到了巨大的作用。

        我们可以用function<void(T*)>类型接收并储存。同时把它做成一个全省参数,如下:

My_share_ptr(T* ptr, function<void(T*)> det= [](T* p) {delete p; }):_ptr(ptr),_count(new int(1)),_det(det){}

        当需要释放资源的时候不要用delete  _ptr,而是用_det(_ptr)即可。 _count是int*类型正常使用delete释放。

六、源码

#include<iostream>
#include<string>
#include<functional>
#include<memory>
using namespace std;
template<class T>
class My_share_ptr
{
public:My_share_ptr(T* ptr, function<void(T*)> det= [](T* p) {delete p; }):_ptr(ptr),_count(new int(1)),_det(det){}My_share_ptr(const My_share_ptr& sptr){_ptr = sptr._ptr;_count = sptr._count;_det = sptr._det;(*_count)++;}My_share_ptr& operator=(const My_share_ptr& sptr){if (sptr._ptr == _ptr) return *this;if (--(*_count) == 0){_det(_ptr);delete _count;cout << "Delete1()" << endl;}_ptr = sptr._ptr;_count = sptr._count;(*_count)++;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~My_share_ptr(){if (--(*_count) == 0){_det(_ptr);delete _count;cout << "Delete2()" << endl;}}
private:T* _ptr;int* _count;function<void(T*)> _det;
};
int main()
{//My_share_ptr<int> p(new int(8));//auto p2 = p;//My_share_ptr<int> c = p;My_share_ptr<int> p1(new int(8));My_share_ptr<int> p2(new int(7));My_share_ptr<int> p3=p1;p1 = p2;return 0;
}

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

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

相关文章

初识Linux · 信号处理 · 续

目录 前言&#xff1a; 可重入函数 重谈进程等待和优化 前言&#xff1a; 在前文&#xff0c;我们已经介绍了信号产生&#xff0c;信号保存&#xff0c;信号处理的主题内容&#xff0c;本文作为信号处理的续篇&#xff0c;主要是介绍一些不那么重要的内容&#xff0c;第一个…

IPTV智慧云桌面,后台服务器搭建笔记

环境CentOs7.9 &#xff0c;安装宝塔yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh 访问宝塔&#xff0c;修改服务器端口安全组端口 26029 注意&#xff01;&#xff01;&#xff01;&#xff01…

模型的评估指标——IoU、混淆矩阵、Precision、Recall、P-R曲线、F1-score、mAP、AP、AUC-ROC

文章目录 预测框的预测指标——IoU&#xff08;交并比&#xff09;分类预测指标混淆矩阵&#xff08;Confusion Matrix&#xff0c;TP、FP、FN、TN)Precision&#xff08;精度&#xff09;Recall&#xff08;召回率&#xff09;P-R曲线F1-scoreTPR、TNR、FPR、FNRROC曲线下面积…

本草智控:中药实验管理的智能时代

3系统分析 3.1可行性分析 通过对本中药实验管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本中药实验管理系统采用SSM框架&#xff0c;JAVA作为开发语…

父组件提交时让各自的子组件验证表格是否填写完整

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 父组件中有三个表格&#xff0c;表格中时输入框&#xff0c;有些输入框是必填的&#xff0c;在父组件提交时需要验证这三个表格的必填输入框中是否有没填写的。 原因分析&#xff1a; 提示&#xff1a…

嘴尚绝卤味独特的口感

在餐饮行业里&#xff0c;嘴尚绝卤味无疑是一颗璀璨的明星。自2021年8月7日创立以来&#xff0c;这个品牌就以其独特的口感和制作工艺赢得了众多食客的青睐。嘴尚绝卤味&#xff0c;作为四川优优熊猫餐饮管理有限公司旗下的主打品牌&#xff0c;专注于提供高品质的休闲佐食&…

JDK17 安装使用

一、Java JDK&#xff08;Java Development Kit&#xff09; 它是开发、运行Java应用程序所需的各种工具和库的集合。 二、JDK 1.8&#xff08;也称为Java 8&#xff09;和JDK 17是两个重要的版本 这两个版本在语言特性、性能优化和安全性方面都有所不同。 1、语言特性 …

解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题

解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题 Chapter1 解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题 Chapter1 解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题 目前使用的是三星4K显示屏&#xff0c;屏幕分辨率太高了&#xff0c;导致VMWare Workst…

uniapp 微信小程序地图标记点、聚合点/根据缩放重合点,根据缩放登记显示气泡marik标点

如图&#xff0c;如果要实现上方的效果&#xff1a; 上方两个效果根据经纬度标记点缩放后有重复点会添加数量 用到的文档地址https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.addMarkers.htmlMapContext.addMarkers(Object object) 添加标记点Ma…

第6章详细设计 -6.7 PCB工程需求表单

6.7 PCB工程需求表单 PCB工程需求表是PCB设计的入口条件&#xff0c;以一块单板为例&#xff0c;表6.2所示的PCB工程需求表单明确了Signal Integrity&#xff08;SI&#xff0c;信号完整性&#xff09;和Power Integrity&#xff08;PI&#xff0c;电源完整性&#xff09;的要…

Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系

一.什么是Spring&#xff1f;它解决了什么问题&#xff1f; 1.1什么是Spring&#xff1f; Spring&#xff0c;一般指代的是Spring Framework 它是一个开源的应用程序框架&#xff0c;提供了一个简易的开发方式&#xff0c;通过这种开发方式&#xff0c;将避免那些可能致使代码…

重构Action-cli前端脚手架

一、概述 最近一年&#xff0c;为了满足公司业务开发&#xff0c;解决重复搭建项目繁琐过程&#xff0c;自己开发了一个前端脚手架&#xff0c;并发布到npm。随着时间的推移&#xff0c;发现之前的版本存在很多问题&#xff0c;有些功能做不到位&#xff0c;而且代码也不是很规…

Kotlin return与return@forEachIndexed

Kotlin return与returnforEachIndexed fun main() {val data arrayOf(0, 1, 2, 3, 4)println("a")data.forEachIndexed { index, v ->if (v 2) {//类似while循环中的continue//跳过&#xff0c;继续下一个forEachIndexed迭代returnforEachIndexed}println("…

Springboot基于GIS的旅游信息管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

《Django 5 By Example》阅读笔记:p645-p650

《Django 5 By Example》学习第8天&#xff0c;p645-p650总结&#xff0c;总计6页。 一、技术总结 1.django-rest-framework (1)serializer p648, Serializer: Provides serialization for normal Python class instances。Serializer又细分为Serializer, ModelSerializer,…

5个有效的华为(HUAWEI)手机数据恢复方法

5个有效的手机数据恢复方法 华为智能手机中的数据丢失比许多人认为的更为普遍。发生这种类型的丢失有多种不同的原因&#xff0c;因此数据恢复软件的重要性。您永远不知道您的智能手机何时会在这方面垮台&#xff1b;因此&#xff0c;预防总比哀叹好&#xff0c;这就是为什么众…

【微软:多模态基础模型】(4)统一视觉模型

欢迎关注[【youcans的AGI学习笔记】](https://blog.csdn.net/youcans/category_12244543.html&#xff09;原创作品 【微软&#xff1a;多模态基础模型】&#xff08;1&#xff09;从专家到通用助手 【微软&#xff1a;多模态基础模型】&#xff08;2&#xff09;视觉理解 【微…

鸿蒙网络编程系列48-仓颉版UDP回声服务器示例

1. UDP回声服务器简介 回声服务器指的是这样一种服务器&#xff0c;它接受客户端的连接&#xff0c;并且把收到的数据原样返回给客户端&#xff0c;本系列的第2篇文章《鸿蒙网络编程系列2-UDP回声服务器的实现》中基于ArkTS语言在API 9的环境下实现了UDP回声服务器&#xff0c…

微博短链接平台-项目测试用例设计(Xmind)

技术栈&#xff1a;Spring BootMyBatis-PlusRedisShardingSphereSentinel 项目描述&#xff1a;微博短链接平台&#xff0c;提供了一个高效、安全和可靠的短链接管理平台。完成较长链接转换为短链接场景。比 如&#xff0c;受微博发送博文长度限制&#xff0c;仅能发表150字&am…

前端基础(四十一):实时获取麦克风音量

效果 源码 <button id"open">打开麦克风</button> <button id"close">关闭麦克风</button><input id"range" type"range" min"0" max"100" value"0" />let _mediaStrea…