【C++智能指针】
智能指针
- 为什么使用智能指针?
- 概念
- 分类
- auto_ptr
- unique_ptr
- shared_ptr
- 循环引用
- weak_ptr
为什么使用智能指针?
考虑以下场景:
void div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void Func()
{int* ptr=new int;div();delete ptr;
}
int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}
Func()函数中我们在堆上申请了资源(new int对象),当div()调用抛异常时,程序会根据异常处理机制,找到最近的catch捕获异常,这使得程序没有进行到释放资源就结束(未delete int 对象),进而会导致内存泄漏。长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死,所以需要一定的方法来优化。
异常处理机制
- 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则调到catch的地方进行处理。
- 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
- 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(…)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。
- 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
常用的解决方案是:
1、事前预防型。如智能指针等。
2、事后查错型。如泄漏检测工具。
概念
智能指针是一种特殊的指针类型,用于自动管理动态分配的内存。相比于传统的裸指针,智能指针提供了更安全、更方便的内存管理方式。
智能指针的原理主要基于对象的生命周期管理和引用计数机制。通过在自动释放内存和避免悬挂指针方面提供便利,它们可以帮助开发人员更轻松地处理动态内存分配和释放的问题。
RAII(Resource Acquisition Is Initialization)
对象的生命周期控制程序资源的技术,在对象构造时获取资源,在对象析构时释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
1.不需要显式地释放资源。(对象析构了资源就释放了)
2.采用这种方式,对象所需的资源在其生命期内始终保持有效。(资源和对象绑定)
分类
在C++98,有智能指针auto_ptr,但在C++11后,auto_ptr被弃用。标准库中提供了三种常见的智能指针:unique_ptr、shared_ptr和weak_ptr。
auto_ptr
auto_ptr是一种独占所有权的智能指针,实现原理是管理权的转移。当一个auto_ptr将其所有权转移给另一个auto_ptr时,原始的auto_ptr将不再拥有对资源的所有权,即两个auto_ptr不能管理同一份资源。
缺陷:
1.拷贝和赋值操作会改变资源的所有权
auto_ptr<string> p1(new string("hhe"));
auto_ptr<string> p2(new string("aab"));
p1=p2;
//p2赋值给p1后,p1释放掉管理"hhe"的指针并接收p2交给它的管理"aab"的指针
//p2交付完毕后释放自己内部的指针并置NULL。
2.指针被悬空成野指针容易解引用造成错误
auto_ptr<int> sp1(new int);
auto_ptr<int> sp2(sp1); // 管理权转移
//sp1悬空
*sp2 = 10;
cout << *sp2 << endl;
cout << *sp1 << endl;
自C++11起,该类模板已被弃用,用更严谨的unique_ptr 取代了auto_ptr
unique_ptr
unique_ptr对auto_ptr的缺陷做了修改,和auto_ptr类似,unique_ptr是一种独占所有权的智能指针。它具有以下特点:
- 提供了唯一拥有的语义,即同一时间只能有一个unique_ptr指向同一块内存。
- 当unique_ptr被销毁时,会自动释放其所拥有的对象。
- 不允许多个unique_ptr指针共享同一块内存,避免了悬空指针和内存泄漏的风险。
- 无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值
unique_ptr<string> p1(new string("hhe"));
unique_ptr<string> p2(new string("aab"));
p1 = p2; // 禁止左值赋值
unique_ptr<string> p3(p2); // 禁止左值赋值构造
unique_ptr<string> p3(std::move(p1));
p1 = std::move(p2); // 使用move把左值转成右值就可以赋值了,效果和auto_ptr赋值一样
下面给出unique_ptr指针的模拟实现:
template<class T>
class unique_ptr
{
public:unique_ptr( T* ptr):_ptr(ptr){}~unique_ptr(){if(_ptr)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;
}
shared_ptr
为了解决一份资源只能被一个智能指针管理的问题,新增了shared_ptr智能指针。shared_ptr是一种共享所有权的智能指针。它使用引用计数的方式来跟踪对象的引用数量。每次创建shared_ptr时,引用计数增加;每次销毁或重置shared_ptr时,引用计数减少。当引用计数变为0时,shared_ptr会自动释放所管理的对象的内存。
引用计数
因为shared_ptr支持一份资源被多个指针管理,未避免对一块空间资源的重复释放,shared_ptr不必在每次析构时都释放资源,而是在管理该资源的最后一个shared_ptr析构时释放资源。那如何知道一个资源被几个shared_ptr管理?析构时如何知道该指针是最后一个管理该资源的指针?这些问题需要在被管理的资源对象上装一个计数器,供这些shared_ptr共享。
当创建shared_ptr时,会同时创建一个控制块,并将所管理的对象指针和引用计数等信息存储在该控制块中。多个shared_ptr可以共享同一个控制块,通过引用计数来追踪所有引用该对象的shared_ptr个数。
template<class T>class shared_ptr{shared_ptr(T* ptr=nullptr) :_ptr(ptr), _pcount(new int(1)){}template<class D>shared_ptr(T* ptr, D del) :_ptr(ptr), _pcount(new int(1)) ,_del(del){}~shared_ptr(){if (--(*_pcount) == 0){cout << "delete:" << _ptr << endl;del(_ptr);delete _pcount;} }T& operator*(){return *_ptr;}T* operator->(){return _ptr;}shared_ptr(const shared_ptr<T>& sp) {_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (this != &sp){--(*_pcount);if (*_pcount == 0){delete _ptr;delete _pcount;}_ptr = sp._ptr;_pcount = sp._pcount;*_pcount++;}return *this;}int use_count(){return *_pcount;}T* get(){return _ptr;}private:T* _ptr;int* _pcount;function<void(T*)> _del = [](T* ptr) {delete ptr;};};
循环引用
shared_ptr在使用不当容易引发循环引用问题。循环引用就是一组对象彼此持有对方的智能指针,从而导致它们的引用计数永远不会变为零。当存在循环引用时,智能指针可能会导致内存泄漏,因为循环引用会阻止引用计数减到零。这将导致相关的内存资源无法被释放,从而造成内存泄漏。
下面是一个产生了循环引用问题的示例:
struct Father
{
...//此处省略成员变量及函数shared_ptr<Child> child;
}
struct Child
{
...//此处省略成员变量及函数shared_ptr<Father> father;
}
//调用
shared_ptr<Father> a(new Father);
shared_ptr<Child> b(new Child);
a->child=b;
b->father=a;
循环计数情况如下图所示:
weak_ptr
weak_ptr是一种弱引用的智能指针,通过使用weak_ptr作为循环引用中的其中一个对象,可以打破循环引用并允许相关对象被正确地销毁。它可以观测shared_ptr所管理的对象,并不会增加引用计数。当最后一个shared_ptr被销毁时,即使还有weak_ptr存在,也不会阻止所管理对象的内存释放。(不参与资源的管理,解决智能指针交叉使用问题)
struct Father
{
...//此处省略成员变量及函数weak_ptr<Child> child;
}
struct Child
{
...//此处省略成员变量及函数weak_ptr<Father> father;
}
使用weak_ptr可以有效避免循环引用问题。
相关文章:

【C++智能指针】
智能指针 为什么使用智能指针?概念分类auto_ptrunique_ptrshared_ptr循环引用weak_ptr 为什么使用智能指针? 考虑以下场景: void div() {int a, b;cin >> a >> b;if (b 0)throw invalid_argument("除0错误");return…...

gcc/g++使用格式+各种选项,预处理/编译(分析树,编译优化,生成目标代码)/汇编/链接过程(函数库,动态链接)
目录 gcc/g--编译器 介绍 使用格式 通用选项 编译选项 链接选项 程序编译过程 预处理(宏替换) 编译 (生成汇编) 分析树(parse tree) 编译优化 删除死代码 寄存器分配和调度 强度削弱 内联函数 生成目标代码 汇编 (生成二进制代码) 链接(生成可执行文件) 函…...

OSPF复习(2)
目录 一、LSA的头部 二、6种类型的LSA(课堂演示) 1、type1-LSA:----重要且复杂 2、type2-LSA: 3、type3-LSA: 4、type4-LSA: 5、type5-LSA: 6、type7-LSA: 三、OSPF的网络类…...

FPGA时序分析与约束(9)——主时钟约束
一、时序约束 时序引擎能够正确分析4种时序路径的前提是,用户已经进行了正确的时序约束。时序约束本质上就是告知时序引擎一些进行时序分析所必要的信息,这些信息只能由用户主动告知,时序引擎对有些信息可以自动推断,但是推断得到…...

sqlite3 关系型数据库语言 SQL 语言
SQL(Structured Query Language)语言是一种结构化查询语言,是一个通用的,功能强大的关系型数据库操作语言. 包含 6 个部分: 1.数据查询语言(DQL:Data Query Language) 从数据库的二维表格中查询数据,保留字 SELECT 是 DQL 中用的最多的语句 2.数据操作语言(DML) 最主要的关…...
spring boot中的多环境配置
1.切换环境 spring:profiles:include: devactive: dev的作用是为了启动某个环境,两个作用基本一致, 环境定义如下: spring:profiles: dev或者是查找application-dev.yml这个文件的所有配置 2.加载文件 spring:config:import:- optional:f…...

python3 阿里云api进行巡检发送邮件
python3 脚本爬取阿里云进行巡检 不确定pip能不能安装上,使用时候可以百度一下,脚本是可以使用的,没有问题的 太长时间了,pip安装依赖忘记那些了,使用科大星火询问了下,给了下面的,看看能不能使…...

【Linux】安装使用Nginx负载均衡,并且部署前端项目
目录 一、Nginx概述 1. 什么 2. 背景 3. 作用 二、Nginx负载均衡 1. 讲述 2. 使用 1. 下载 2. 安装 3. 负载均衡 三、前端部署 1. 准备 2. 部署 一、Nginx概述 1. 什么 Nginx是一个高性能的开源Web服务器和反向代理服务器。它具有轻量级、高并发、低内存消耗的…...
k8s中 pod 或节点的资源利用率监控
pod 或节点的资源利用率监控 1 简介2 Kubectl Top介绍3 生效kubectl top命令3.1 下载配置components.yaml3.2 修改配置components.yaml参数3 kubectl top 应用3.1 查看node节点的资源占⽤率3.2 查看pod的资源占⽤率1 简介 通过Kubectl Top命令,可以查看你k8snode节点或者pod的…...

订水商城实战教程07-搜索
目录 1 创建数据源2 首页搜索功能3 创建搜索页面4 搭建搜索结果页面总结 上一篇我们讲解了店铺信息的展示功能,本篇讲解一下搜索功能。通常小程序在首页都配置了搜索的功能,输入关键词进行检索,可以在结果页上进行选购。同时还记录了用户的搜…...
stm32内 misc stm32f10x_hd stm32f10x_it stm32f10x_conf关系
STM32启动流程 初始位置 startup_stm32f10x_hd.s 查看源码 ;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** ;* File Name : startup_stm32f10x_hd.s ;* Author : MCD Application Team ;* Version :…...

树结构及其算法-二叉查找树
目录 树结构及其算法-二叉查找树 C代码 树结构及其算法-二叉查找树 二叉树在建立的过程中是根据“左子树 < 树根 < 右子树”的原则建立的,因此只需从树根出发比较键值即可,如果比树根大就往右,否则往左而下,直到相等就找…...

PHP自定义文件缓存实现
文件缓存:可以将PHP脚本的执行结果缓存到文件中。当一个PHP脚本被请求时,先查看是否存在缓存文件,如果存在且未过期,则直接读取缓存文件内容返回给客户端,而无需执行脚本 1、文件缓存写法一,每个文件缓存一…...

猫耳 Android 播放框架开发实践
概述 猫耳FM是中国最大的 95 后声音内容分享平台,是B站重要平台之一,深度合作国内顶级声优工作室,打造了数百部精品广播剧,全站播放总量超过百亿次。 MEPlayer 是猫耳 Android 技术团队研发的一款适用于音视频、直播、特效播放等…...

linux下df -h 命令一直卡住的解决方法
在Linux中,偶尔遇到用 df -h 查看磁盘情况时,一直卡住无法显示结果。 解决方法: 1、首先使用strace追踪到底执行到哪里卡住 $ strace df -h 2、如果没有strace命令则进行安装 $ yum install strace -y 3、显示出卡住的地方,如…...
系统架构设计热点知识
系统架构设计师考点包括以下内容: 1. 系统设计和架构思想. 了解系统设计和架构的基本概念和思想,特别是面向服务架构(SOA)、微服务架构、云架构、事件驱动架构、响应式架构等。 系统设计是指在软件项目中,确定系统结…...

2023-在mac下安装Homebrew的国内镜像
mac安装Homebrew的国内镜像 尝试使用其他下载源:GitHub 可能会受到访问限制,尝试使用其他镜像或下载源。您可以使用清华大学、中科大或阿里云的 Homebrew 镜像,以提高下载速度和可靠性。例如,可以使用阿里云的镜像来安装 Homebre…...

Ubuntu 20.04设置虚拟内存 (交换内存swap)解决内存不足
数据库服务器程序在运行起来之后,系统内存不足。 在系统监控中发现,当数据库服务程序启动后,占用了大量内存空间,导致系统的剩余的内存往往只有几十MB。 在ubuntu系统中,swap空间就是虚拟内存,所以考虑在磁…...

RabbitMQ-死信交换机和死信队列
1. 简介 1.1 DLX简介 DLX: Dead-Letter-Exchange 死信交换器,死信邮箱 当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX。 如下图所示: 其实死信队列就是一个普通的交换机,有些队列的消息…...

[HNCTF 2022 WEEK2]easy_include 文件包含遇上nginx
这道纯粹记录 完全没想到 <?php //WEB手要懂得搜索if(isset($_GET[file])){$file $_GET[file];if(preg_match("/php|flag|data|\~|\!|\|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\|\/i", $file)){die("error");}include($file); }else{highlight_file(__…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...