【C++】智能指针:auto_ptr、unique_ptr、share_ptr、weak_ptr(技术介绍 + 代码实现)(待更新)
文章目录
- 0. 概述
- 智能指针,智能在哪儿?
- RAII 的介绍
- 四个智能指针的特点:
- 1. auto_ptr(C++98)
- 🐎核心功能的简单实现
- 2. unique_ptr(C++11)
- 🐎核心功能的简单实现
- 3. shared_ptr(C++11)
- 🐎核心功能的简单实现
0. 概述
智能指针,智能在哪儿?
- 使用了模板类,建立的是 智能指针对象,自动调用智能指针类型的构造和析构函数。也就是说,对于动态开辟的空间如果用智能指针保存,就不需要手动释放啦,极大程度降低了内存泄漏的风险。
- 这样利用对象生命周期进行程序资源控制的技术就是 RAII。
- 对
*
和->
的重载,使 智能指针对象 具有指针的行为能力,能让用户像使用指针一样的使用。
RAII 的介绍
RAII(Resource Acquisition Is Initialization)
是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源;
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
四个智能指针的特点:
我把四个智能指针的特点介绍在前面,你若还有什么细节问题再去具体的栏目下翻找吧~
auto_ptr
:管理权转移,通过拷贝构造函数和赋值重载函数来实现。- 原对象拷贝给新对象的时候,原对象就会被设置为nullptr,此时就只有新对象指向一块资源空间。
- 会出现指针悬空问题。
unique_ptr
:禁用拷贝构造和赋值构造- unique_ptr(unique_ptr&) = delete;
- operator=(unique_ptr&) = delete;
share_ptr
:引用计数- 计数的对象在堆上,所有线程都能访问,因此需要锁保证其安全性
- 会出现循环引用的问题
weak_ptr
:弱关联性- weak_ptr 类的对象它可以指向 shared_ptr,并且不会改变 shared_ptr 的引用计数
1. auto_ptr(C++98)
核心功能:管理权转移
管理权转移的同时也会导致 原指针悬空,容易造成野指针问题,不推荐使用。
🐎核心功能的简单实现
namespace ttang
{template<class T>class auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}~auto_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 无力吐槽的神拷贝...// 管理权转移:导致的是原来的指针悬空,很多公司明令禁止使用 auto_ptrauto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){ap._ptr = nullptr;}T* operator=(auto_ptr<T>& ap){T* tmp = ap._ptr;ap._ptr = nullptr;return tmp;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
--------------------------------------------------void test_auto(){auto_ptr<int> ap1(new int(1));auto_ptr<int> ap2(ap1);*ap1 = 1; // err...管理权转移以后导致ap1悬空,不能访问*ap2 = 1;}
}
2. unique_ptr(C++11)
核心功能:防拷贝(
= delete
声明拷贝构造和复制重载)
unique_ptr 的指针,简单粗暴,是防了拷贝,不过也只解决了不需要拷贝的场景。
(ps:从 boost 里面吸收来的)
(pps:需要拷贝的场景就需要使用到接下来会介绍的 shared_ptr 和 weak_ptr 了)
🐎核心功能的简单实现
namespace ttang
{template<class T>class unique_ptr{private:T* _ptr;public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}// C++11思路:设置不许再实现了,语法直接支持的(不需要私有了)unique_ptr(const unique_ptr<T>& up) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete; // 严格来说赋值也封了更好一点// C++98思路:只声明不实现,但是用的人可能会在外面强行定义,所以再加一条,声明为私有//private:// unique_ptr(const unique_ptr<T>& up);// unique_ptr<T>& operator=(const unique_ptr<T>& up);};
--------------------------------------------------void test_unique(){unique_ptr<int> up1(new int(1));unique_ptr<int> up2(up1); // err...}
}
3. shared_ptr(C++11)
核心功能:引用计数
之前在一开始的概述部分介绍了两个智能指针为什么智能的原因,走到了 shared_ptr,我们的智能指针就真的更神了,他甚至还引申出 智能指针三大件 的说法:
- RAII
- 想指针一样使用
- 可以拷贝(浅拷贝!!)
他可以对同一个对象拷贝的同时,还可以在最后一个智能指针使用完毕后调用其析构函数,使用的是引用计数的技术。
在具体对 shared_ptr 实现之前,对于这里的 引用计数,可以稍微探讨一下:
1)如果要使用引用计数,设置一个静态变量 count 行不行呢?不行,因为静态变量属于所有对象。而 每实例化一个对象都可能多有个资源,每个资源应该配对一个引用计数。
2)如果是多个线程去调用引用计数,还需要保证其线程安全,那就加个锁吧。
3)计数加锁后,shared_ptr 本身就会是线程安全的,但是他生成的对象不是线程安全的。
🐎核心功能的简单实现
namespace ttang
{template<class T>class shared_ptr{private:T* _ptr;int* _pcount;mutex* _pmtx; // 锁也得是指针,因为是多个指针指向同一把锁function<void(T*)> _del = [](T* ptr) { // 解决对 deletor 的保存问题,需要一个缺省的!!cout << "lambda delete:" << ptr << endl;delete ptr;};public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)) // 每个资源都分配一个引用计数count, _pmtx(new mutex) // 每个资源都有一把锁,保证自己资源计数安全{}// 定制删除器(通过仿函数实现的!--是可调用对象,所以我们拿的一个function定义_del)template<class D>shared_ptr(T* ptr, D del): _ptr(ptr), _pcount(new int(1)), _pmtx(new mutex), _del(del){}~shared_ptr(){Release();}void Release(){_pmtx->lock();bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr){//cout << "delete:" << _ptr << endl;//delete _ptr;_del(_ptr); // 如果_del 不给缺省的话,这里默认的构造可能会出问题}delete _pcount;deleteFlag = true;// delete _pmtx; 锁也要释放的呀,可以下面又要解锁。如何解决?}_pmtx->unlock();if (deleteFlag){delete _pmtx;}}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}// 正常赋值:// sp1 = sp4; 被赋值sp1的肯定要--,sp4要++// 自己给自己赋值:// sp1 = sp1; 自己给自己 // sp1 = sp2;(管理同样资源) 也是自己给自己 if(&sp != this)不得行哦,防不了这一种shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;AddCount();}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}int use_count(){return *_pcount;}};
}
// 跟库里不一样,简易版的蛤template<class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}private:T* _ptr;};void test_shared(){shared_ptr<int> sp1(new int(1));shared_ptr<int> sp2(sp1);shared_ptr<int> sp3(sp2);shared_ptr<int> sp4(new int(10));//sp1 = sp4;sp4 = sp1;sp1 = sp1;sp1 = sp2;}// 多线程的问题!!!!!!!struct Date{int _year = 0;int _month = 0;int _day = 0;};// shared_ptr本身是线程安全的,因为计数是加锁保护// shared_ptr管理的对象是否是线程安全?不是void SharePtrFunc(ttang::shared_ptr<Date>& sp, size_t n, mutex& mtx){//cout << sp.get() << endl;//cout << &sp << endl;for (size_t i = 0; i < n; ++i){// 这里智能指针拷贝会++计数,智能指针析构会--计数,这里是线程安全的。ttang::shared_ptr<Date> copy(sp);mtx.lock();sp->_year++;sp->_day++;sp->_month++;mtx.unlock();}}void test_shared_safe(){ttang::shared_ptr<Date> p(new Date);cout << p.get() << endl;const size_t n = 10000;mutex mtx;thread t1(SharePtrFunc, ref(p), n, ref(mtx)); // 线程中以引用传递对象参数,必须加一个ref(),是一个库函数,否则会认为是传值传参会报错。thread t2(SharePtrFunc, ref(p), n, ref(mtx)); // 13 底下可以检测,19 是直接报错//cout << &p << endl; t1.join();t2.join();cout << p.use_count() << endl;cout << p->_year << endl;cout << p->_month << endl;cout << p->_day << endl;}// 循环引用的问题!!!!!!!struct ListNode{/*ListNode* _next;ListNode* _prev;*///ttang::shared_ptr<ListNode> _next;//ttang::shared_ptr<ListNode> _prev;ttang::weak_ptr<ListNode> _next; // weak_ptr解决循环引用的问题,他可以指向资源,但是他不参与管理,不增加应用计数ttang::weak_ptr<ListNode> _prev;int _val;~ListNode(){cout << "~ListNode()" << endl;}};// 循环引用场景:// 有智能指针 n1 和 n2 指向结构体,里面包含的智能指针又相互指了!!出现闭环,导致_count无法到0无法析构!!// 循环引用--》内存泄漏// 主要原理:成员的生命周期,取决于对象的生命周期,对象的生命周期结束则成员调用析构函数,成员源于被另一个智能指针管理,无法释放,形成闭环。void test_shared_cycle(){/*ListNode* n1 = new ListNode;ListNode* n2 = new ListNode;n1->_next = n2;n2->_prev = n1; // 会出现抛异常未释放的情况,所以我们现在并不推荐这么写!// 那怎么写呢?--》智能指针~delete n1;delete n2;*/ttang::shared_ptr<ListNode> n1(new ListNode); // std 里面只能这样显示的调构造ttang::shared_ptr<ListNode> n2(new ListNode);cout << n1.use_count() << endl;cout << n2.use_count() << endl;n1->_next = n2; // 链接的时候,智能指针(n2)怎么赋值给原生指针(_next)呢?把ListNode里的指针改成智能指针就好了!n2->_prev = n1; cout << n1.use_count() << endl;cout << n2.use_count() << endl;}// weak_ptr// 1、他不是常规的智能指针,不支持RAII// 2、支持像指针一样// 3、专门设计出来,辅助解决shared_ptr的循环引用问题// weak_ptr可以指向资源,但是他不参与管理,不增加引用计数// 定制删除器 -- 可调用对象template<class T>struct DeleteArray{void operator()(T* ptr){cout << "void operator()(T* ptr)" << endl;delete[] ptr;}};//void test_shared_deletor()//{// std::shared_ptr<Date> spa1(new Date[10], DeleteArray<Date>());// std::shared_ptr<Date> spa2(new Date[10], [](Date* ptr){// cout << "lambda delete[]"<<ptr << endl;// delete[] ptr; // });// std::shared_ptr<FILE> spF3(fopen("Test.cpp", "r"), [](FILE* ptr){// cout << "lambda fclose" << ptr << endl;// fclose(ptr);// });//}void test_shared_deletor(){ttang::shared_ptr<Date> sp0(new Date);ttang::shared_ptr<Date> spa1(new Date[10], DeleteArray<Date>());ttang::shared_ptr<Date> spa2(new Date[10], [](Date* ptr) {cout << "lambda delete[]:" << ptr << endl;delete[] ptr;});ttang::shared_ptr<FILE> spF3(fopen("Test.cpp", "r"), [](FILE* ptr) {cout << "lambda fclose:" << ptr << endl;fclose(ptr);});}
}// 智能指针的个间区别 unique、share、weak
相关文章:

【C++】智能指针:auto_ptr、unique_ptr、share_ptr、weak_ptr(技术介绍 + 代码实现)(待更新)
文章目录 0. 概述智能指针,智能在哪儿?RAII 的介绍四个智能指针的特点: 1. auto_ptr(C98)🐎核心功能的简单实现 2. unique_ptr(C11)🐎核心功能的简单实现 3. shared_ptr&…...

nodejs+vue全国公考岗位及报考人数分析
传统的搜索引擎尽管解决了信息搜索问题,但无法进行有效的数据分析和优质资源的获取。并且,人们的需求不同,数据的要求也不同。为了解决这一问题,定向抓取数据的爬虫诞生了。它的诞生把人们从重复性的劳动中解放出来,节…...

【0基础学Java第二课】数据类型与变量
2. 数据类型与变量 2.1 字面常量2.2 数据类型2.3 变量2.3.1 变量概念2.3.2 语法格式 2.4 整型变量2.4.1 整型变量2.4.2 长整型变量2.4.3 短整型变量2.4.4 字节型变量 2.5 浮点型变量2.6 字符型2.7 布尔型变量2.8 类型转换2.9 类型提升2.10 字符串类型2.10.1 字符串拼接操作符 2…...

Pytorch整体工作流程代码详解(新手入门)
一、前言 本文详细介绍Pytorch的基本工作流程及代码,以及如何在GPU上训练模型(如下图所示)包括数据准备、模型搭建、模型训练、评估及模型的保存和载入。 适用读者:有一定的Python和机器学习基础的深度学习/Pytorch初学者。 本文…...

读图数据库实战笔记02_图数据建模
1. 概念 1.1. 实体 1.1.1. 通常用名词来表示 1.1.2. 描述一个领域中的事物或者事物类型 1.1.2.1. 汽车 1.1.2.2. 用户 1.1.2.3. 地理位置 1.1.3. 在逻辑模型和技术实现过程中,实体通常会变成“顶点” 1.2. 关系 1.2.1. 用动词(或动词短语&#…...

竞赛 深度学习卫星遥感图像检测与识别 -opencv python 目标检测
文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐…...

对Happens-Before的理解
Happens-Before Happens-Before 是一种可见性模型,也就是说,在多线程环境下。原本因为指令重排序的存在会导致数据的可见性问题,也就是 A 线程修改某个共享变量对 B 线程不可见。因此,JMM 通过 Happens-Before 关系向开发人员提供…...

分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测
分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测 目录 分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双…...

Java面试八股文之暑假合集
八股文暑假合集 基础篇二分查找 java基础篇7月12号面向对象和面向过程的区别重载和重写String 7月13号自动装箱和拆箱静态方法构造方法成员变量和局部变量对象引用和对象实例返回值 与equals(重要)hashcode()和equals()HashMap 7月16号线程,进程和程序final关键字的…...

竞赛选题 深度学习卫星遥感图像检测与识别 -opencv python 目标检测
文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐…...

机器学习第一周
一、概述 机器学习大致会被划分为两类:监督学习,无监督学习 1.1 监督学习 监督学习其实就是,给计算机一些输入x和正确的输出y(训练数据集),让他总结x->y的映射关系,从而给他其他的输入x&a…...

大数据采集技术与预处理学习一:大数据概念、数据预处理、网络数据采集
目录 大数据概念: 1.数据采集过程中会采集哪些类型的数据? 2.非结构化数据采集的特点是什么? 3.请阐述传统的数据采集与大数据采集的区别? 4.大数据采集的数据源有哪些?针对不同的数…...

MySQL - 为什么索引结构默认使用B+树,而不是其他?
B-Tree的缺点: 范围查询效率相对较低:虽然B-Tree支持范围查询,但在实际操作中可能需要进行多次树遍历,性能较差。磁盘空间利用不高:B-Tree中的非叶子节点也存储数据,导致磁盘空间利用率相对较低。更复杂的平…...

信息系统项目管理师教程 第四版【第3章-信息系统治理-思维导图】
信息系统项目管理师教程 第四版【第3章-信息系统治理-思维导图】...

2023.NET技术沙龙知识学习笔记
目录 一.Bootstrap Blazor UI组件库企业级应用介绍1.Blazor是什么2.为什么要用Blazor3.Bootstrap Blazor是什么 二.使用WebAssembly运行、扩展.NET应用程序1.WebAssembly简介2.WebAssembly的起源3.为什么选择二进制格式?4.WebAssembly与传统JavaScript的对比5.执行速…...

Golang教程——配置环境,再探GoLand
文章目录 一、Go是什么?二、环境配置验证配置环境变量 三、安装开发者工具GoLand四、HelloGolang 一、Go是什么? Go(也称为Golang)是一种开源的编程语言,由Google开发并于2009年首次发布。Go语言旨在提供一种简单、高…...

C++之lambda匿名、using、typedef总结【全】(二百四十九)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...

基于SpringBoot的个人博客系统
基于SpringBootVue的个人博客系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringBootMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 系统公告 博客详情 后台发文 摘要 基于Spring Boot的个人博客系统是一种…...

javascript中的继承
基本术语 本文中,proto [[Prototype]] 原型链 基本思想: 构造函数生成的对象有一个指针(proto)指向构造函数的原型。如果将构造函数1的原型指向另一个构造函数2的实例,则构造函数1的实例__proto__.proto 指向了构…...

智能问答技术在百度搜索中的应用
作者 | Xiaodong 导读 本文主要介绍了智能问答技术在百度搜索中的应用。包括机器问答的发展历程、生成式问答、百度搜索智能问答应用。欢迎大家加入百度搜索团队,共同探索智能问答技术的发展方向,文末有简历投递方式。 全文6474字,预计阅读时…...

STM32F4X SDIO(一) SD卡介绍
STM32F4X SDIO(一) SD卡介绍 SD卡分类外观分类容量分类传输速度分类 在之前的章节中,讲过有关嵌入式的存储设备,有用I2C驱动的EEPROM、SPI驱动的FLASH和MCU内部的FLASH,这类存储设备的优点是操作简单,但是缺…...

10分钟了解JWT令牌 (JSON Web)
10分钟了解JSON Web令牌(JWT) JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。今天给大家介绍JWT的原理和用法。 1.跨域身份验证 Internet服务无法与用户身份验证分开。一般过程如下。 1.用户向服务器发送用户名和密码。…...

【经验总结】ECU系统休眠后通过诊断报文唤醒ECU且唤醒网络后快发NM报文
目录 前言 正文 1.CanNM状体机分析 2.ComM状态机分析 3.解决方案 4.总结 前言...

基于Android 10系统的ROC-RK3399-PC Pro源码编译
基于Android 10系统的ROC-RK3399-PC Pro源码编译 一、开发环境搭建二、下载Android 10 SDK三、编译Android 10 SDK ROC-RK3399-PC Pro资料下载处:https://www.t-firefly.com/doc/download/145.html一、开发环境搭建 Android 10 SDK的编译对PC机的要求不低ÿ…...

网络滤波器/网络滤波器/脉冲变压器要怎样进行测试,一般要测试哪些参数?
Hqst华强盛导读:网络滤波器/网络滤波器/脉冲变压器要怎样进行测试,一般要测试哪些参数?测试网络滤波器的测试方法和步骤如何,需用到哪些测试工具和仪器设备呢? 一,网络流量的监控和过滤能力测试&am…...

基于vue天气数据可视化平台
目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…...

Go 语言常见的 ORM 框架
ORM(Object-Relational Mapping)是一种编程技术,用于将面向对象编程语言中的对象模型和关系数据库中的数据模型相互映射。ORM框架可以把数据操作从 SQL 语句中抽离出来,将关系型数据库中的表映射成对象,通过面向对象的…...

【错误解决方案】ModuleNotFoundError: No module named ‘cPickle‘
1. 错误提示 在python程序中试图导入一个名为cPickle的模块,但Python提示找不到这个模块。 错误提示:ModuleNotFoundError: No module named cPickle 2. 解决方案 实际上,cPickle是Python的pickle模块的一个C语言实现,通常用于…...

NodeJS14.18.0 安装,以及安装相应版本node-sass
安装了NVM, NodeJS 14.18.0 安装nvm 到c:\nvm目录 务必!!!!!!!! nvm文档手册 - nvm是一个nodejs版本管理工具 - nvm中文网 编辑c:\nvm\settings.txt添加 node_mirror: CNPM Binar…...

cosover是什么?crossover23又是什么软件
cosover是篮球里的过人技巧。 1.crossover在篮球中的本意是交叉步和急速交叉步。crossover 是篮球术语,有胯下运球、双手交替运球,交叉步过人、急速大幅度变向等之意。 2.在NBA里是指包括胯下运球、变向、插花在内的过人的技巧。 NBA有很多著名的Cross…...