当前位置: 首页 > news >正文

c++系列——智能指针

1.智能指针的使用及原理

1.1 RAII

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

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

这种做法有两大好处:

1.不需要显式地释放资源。

2.采用这种方式,对象所需的资源在其生命期内始终保持有效。

// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}private:T* _ptr;
};
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void Func()
{ShardPtr<int> sp1(new int);ShardPtr<int> sp2(new int);cout << div() << endl;
}
int main()
{try {Func();}catch(const exception& e){cout<<e.what()<<endl;}return 0;
}

1.2 智能指针的原理

上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可 以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其 像指针一样去使用。 

template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr): _ptr(ptr){}
~SmartPtr(){if(_ptr)delete _ptr;}
T& operator*() {return *_ptr;}
T* operator->() {return _ptr;}
private:
T* _ptr;
};
struct Date
{int _year;int _month;int _day;
};
int main()
{
SmartPtr<int> sp1(new int);
*sp1 = 10
cout<<*sp1<<endl;
SmartPtr<int> sparray(new Date);
// 需要注意的是这里应该是sparray.operator->()->_year = 2018;
// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->
sparray->_year = 2018;
sparray->_month = 1;
sparray->_day = 1;
}

总结一下智能指针的原理:

1. RAII特性

2. 重载operator*和opertaor->,具有像指针一样的行为

1.3 std::auto_ptr 

C++98版本的库中就提供了auto_ptr的智能指针。下面演示的auto_ptr的使用及问题。 auto_ptr的实现原理:管理权转移的思想,下面简化模拟实现了一份bit::auto_ptr来了解它的原理

// C++98 管理权转移 auto_ptr
namespace bit
{template<class T>class auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptr<T>& sp):_ptr(sp._ptr){// 管理权转移sp._ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T>& ap){// 检测是否为自己给自己赋值if (this != &ap){// 释放当前对象中资源if (_ptr)delete _ptr;// 转移ap中资源到当前对象中_ptr = ap._ptr;ap._ptr = NULL;}return *this;}~auto_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;
}}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
}
// 结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr
//int main()
//{
// std::auto_ptr<int> sp1(new int);
// std::auto_ptr<int> sp2(sp1); // 管理权转移
//
// // sp1悬空
// *sp2 = 10;
// cout << *sp2 << endl;
// cout << *sp1 << endl;
// return 0;
//}

1.4 std::unique_ptr

C++11中开始提供更靠谱的unique_ptr 

unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原 理 

// C++11库才更新智能指针实现
// C++11出来之前,boost搞除了更好用的scoped_ptr/shared_ptr/weak_ptr
// C++11将boost库中智能指针精华部分吸收了过来
// C++11->unique_ptr/shared_ptr/weak_ptr
// unique_ptr/scoped_ptr
// 原理:简单粗暴 -- 防拷贝
namespace bit
{template<class T>class unique_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;}unique_ptr(const unique_ptr<T>& sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;private:T* _ptr;};
}
//int main()
//{
// /*bit::unique_ptr<int> sp1(new int);
// bit::unique_ptr<int> sp2(sp1);*/
//
// std::unique_ptr<int> sp1(new int);
// //std::unique_ptr<int> sp2(sp1);
//
// return 0;
//}

1.5 std::shared_ptr 

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共 享。

2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减 一。

3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源 

4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

// 引用计数支持多个拷贝管理同一个资源,最后一个析构对象释放资源
namespace bit
{template<class T>class shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pRefCount(new int(1)), _pmtx(new mutex){}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx){AddRef();}void Release(){_pmtx->lock();bool flag = false;if (--(*_pRefCount) == 0 && _ptr){cout << "delete:" << _ptr << endl;delete _ptr;delete _pRefCount;flag = true;}_pmtx->unlock();if (flag == true){delete _pmtx;}}void AddRef(){_pmtx->lock();++(*_pRefCount);_pmtx->unlock();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){//if (this != &sp)if (_ptr != sp._ptr){Release();
_ptr = sp._ptr;_pRefCount = sp._pRefCount;_pmtx = sp._pmtx;AddRef();}return *this;}int use_count(){return *_pRefCount;}~shared_ptr(){Release();}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}private:T* _ptr;int* _pRefCount;mutex* _pmtx;};// 简化版本的weak_ptr实现template<class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;
}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
}

shared_ptr智能指针是线程安全的吗?

 是的,引用计数的加减是加锁保护的。但是指向资源不是线程安全的

 指向堆上资源的线程安全问题是访问的人处理的,智能指针不管,也管不了

 引用计数的线程安全问题,是智能指针要处理的 

std::shared_ptr的线程安全问题通过下面的程序我们来测试shared_ptr的线程安全问题。需要注意的是shared_ptr的线程安全分为两方面:

1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时 ++或--,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2.这样引用计数就错 乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、--是需要加锁 的,也就是说引用计数的操作是线程安全的。

2. 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。 

 

2.C++11和boost中智能指针的关系

C++11和boost中智能指针的关系

1. C++ 98 中产生了第一个智能指针auto_ptr.

2. C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.

3. C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。

4. C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost 的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。

相关文章:

c++系列——智能指针

1.智能指针的使用及原理 1.1 RAII RAII&#xff08;Resource Acquisition Is Initialization&#xff09;是一种利用对象生命周期来控制程序资源&#xff08;如内 存、文件句柄、网络连接、互斥量等等&#xff09;的简单技术。 在对象构造时获取资源&#xff0c;接着控制对资…...

力扣日记10.30-【栈与队列篇】滑动窗口最大值

力扣日记&#xff1a;【栈与队列篇】滑动窗口最大值 日期&#xff1a;2023.10.30 参考&#xff1a;代码随想录、力扣 239. 滑动窗口最大值 题目描述 难度&#xff1a;困难 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只…...

docker与宿主机共享内存通信

docker与宿主机共享内存通信 docker中的进程要与宿主机使用共享内存通信&#xff0c;需要在启动容器的时候指定“–ipchost”选项。然后再编写相应的共享内存的程序&#xff0c;一个跑在宿主机上&#xff0c;另一个跑在docker上面。 宿主机程序准备 shm_data.h #ifndef _SH…...

A股风格因子看板 (2023.10 第13期)

该因子看板跟踪A股风格因子&#xff0c;该因子主要解释沪深两市的市场收益、刻画市场风格趋势的系列风格因子&#xff0c;用以分析市场风格切换、组合风格暴露等。 今日为该因子跟踪第13期&#xff0c;指数组合数据截止日2023-09-30&#xff0c;要点如下 近1年A股风格因子检验统…...

ORB-SLAM3算法2之EuRoc、TUM和KITTI开源数据集运行ORB-SLAM3生成轨迹并用evo工具评估轨迹

文章目录 0 引言1 数据和真值1.1 TUM1.2 EuRoc1.3 KITTI2 ORB-SLAM3的EuRoc示例2.1 纯单目的示例2.2 纯单目的轨迹评估2.3 纯双目的示例2.4 纯双目的轨迹评估2.5 单目和IMU的示例2.6 单目和IMU的轨迹评估2.7 双目和IMU的示例2.8 双目和IMU的轨迹评估2.9 前四种的评估结果对比3 …...

【蓝桥杯选拔赛真题07】C++小球自由落体 青少年组蓝桥杯C++选拔赛真题 STEMA比赛真题解析

目录 C/C++小球自由落体 一、题目要求 1、编程实现 2、输入输出 二、算法分析...

期中考成绩一键私发

作为一名教师&#xff0c;期中考试后最繁忙的事情之一就是发布成绩。每个学生都希望尽快知道自己的成绩&#xff0c;而作为老师&#xff0c;我们需要一种更高效、更方便的方式来完成这项任务。今天&#xff0c;我就来给大家介绍一种成绩查询系统&#xff0c;让我们一起告别繁琐…...

idea中Run/Debug Python项目报错 Argument for @NotNull parameter ‘module‘ of ...

idea中Run/Debug Python项目报错 Argument for NotNull parameter module of ... idea中运行Python项目main.py时报错&#xff1a; Error running main: Argument for NotNull parameter module of com/intellij/openapi/roots/ModuleRootManager.getInstance must not be nu…...

计算机网络第3章-TCP协议(2)

TCP拥塞控制 TCP拥塞控制的三种方式&#xff1a; 慢启动、拥塞避免、快速恢复 慢启动 当一条TCP连接开始时&#xff0c;cwnd的值是一个很小的MSS值&#xff0c;这使得初始发送速率大约为MSS/RTT。 在慢启动状态&#xff0c;cwnd的值以1个MSS开始并且每当传输的报文段首次被…...

SQL注入——二次注入漏洞

文章目录 SQL注入——二次注入漏洞1. 二次注入原理2. 二次注入需要具备的两个条件3. 二次注入实例4. 总结 SQL注入——二次注入漏洞 1. 二次注入原理 在第一次插入恶意数据的时候&#xff0c;只是对其中的特殊字符进行了转义&#xff0c;在写入数据库的时候还是原来的字符&am…...

【c++|opencv】二、灰度变换和空间滤波---1.灰度变换、对数变换、伽马变换

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 灰度变换、对数变换、伽马变换 1. 灰度变换 #include <iostream> #include <opencv2/opencv.hpp>using namespace std; using namespace c…...

【vue3】子传父-事件总线-mitt(子组件派发事件,父组件接收事件和传递的参数)

安装库&#xff1a;cnpm install mitt 封装 eventbus.ts&#xff1a; src->utils->eventbus.ts //eventbus.tsimport mitt from mittconst emitter mitt()export default emitter使用 B2.vue&#xff1a; //B2.vue <template><div class"aa">…...

【杂记】java 大集合进行拆分

日常中需要对一个大的集合进行拆分成多个小集合&#xff0c;其主要思路为&#xff1a; 设置需要拆分多少个小集合 A大集合里面有多少条数据 B计算出每个集合里面有多个条数据 CB/A计算出看是否存在余数 DB%A采用集合(List.subList())的方法对大集合进行拆分,循环A变进行集合拆…...

select...for update 锁表了?

在MySQL中&#xff0c;事务A中使用select...for update where id1锁住了&#xff0c;某一条数据&#xff0c;事务还没提交&#xff0c;此时&#xff0c;事务B中去用select ... where id1查询那条数据&#xff0c;会阻塞等待吗&#xff1f; select...for update在MySQL中&#…...

使用ControlNet生成视频(Pose2Pose)

目录 ControlNet 介绍 ControlNet 14种模型分别是用来做什么的 ControlNet 运行环境搭建 用到的相关模型地址 ControlNet 介绍 ControlNet 是一种用于控制扩散模型的神经网络结构&#xff0c;可以通过添加额外的条件来实现对图像生成的控制。它通过将神经网络块的权重复制到…...

基于Docker使用Minikube

1. 查看并操控Minikube状态信息 Minikube相当于docker中的一个container&#xff0c;可以在Docker Desktop中看到并操控Minikube container的相关状态&#xff1a; 通过以下命令查看当前docker中的container&#xff1a; % docker ps CONTAINER ID IMAGE …...

Stable Diffusion系列(一):古早显卡上最新版 WebUI 安装及简单操作

文章目录 Stable Diffusion安装AnimateDiff插件适配sdxl模型适配 Stable Diffusion使用插件安装界面设置基础文生图加入lora的文生图 Stable Diffusion安装 我的情况比较特殊&#xff0c;显卡版本太老&#xff0c;最高也就支持cuda10.2&#xff0c;因此只能安装pytorch1.12.1&…...

ruoyi框架前端vue部署生产环境教程

前端有子目录&#xff0c;后端有项目名称&#xff0c;请看第3种 第1种 前端nginx没有子目录&#xff0c;后端也没有访问的项目名。这种是最简单的。 vue.config.js 只需要修改target中的IP和端口&#xff0c;就是后端访问的IP和端口 # vue.config.js devServer: {host: 0.…...

leetcode第369周赛

2917. 找出数组中的 K-or 值 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 nums 中的 K-or 是一个满足以下条件的非负整数&#xff1a; 只有在 nums 中&#xff0c;至少存在 k 个元素的第 i 位值为 1 &#xff0c;那么 K-or 中的第 i 位的值才是 1 。 返回 nums …...

如何在维格云中自动新增一行或多行数据?

简介​ 在日常使用维格云中,通常会出现一张表中有数据发生变化时,需要另一张表同时新增一些数据,比如: 项目管理中,每新增一个项目,都要在任务表中产生若干个固定的任务;或一个任务要自动生成若干子任务当一笔订单状态变为成交后,可能要在客户成功表中新增一行记录;帮…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...