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

【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 对象),进而会导致内存泄漏。长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死,所以需要一定的方法来优化。

异常处理机制

  1. 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则调到catch的地方进行处理。
  2. 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
  3. 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(…)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。
  4. 找到匹配的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是一种独占所有权的智能指针。它具有以下特点:

  1. 提供了唯一拥有的语义,即同一时间只能有一个unique_ptr指向同一块内存。
  2. 当unique_ptr被销毁时,会自动释放其所拥有的对象。
  3. 不允许多个unique_ptr指针共享同一块内存,避免了悬空指针和内存泄漏的风险。
  4. 无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值
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++智能指针】

智能指针 为什么使用智能指针&#xff1f;概念分类auto_ptrunique_ptrshared_ptr循环引用weak_ptr 为什么使用智能指针&#xff1f; 考虑以下场景&#xff1a; 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&#xff08;课堂演示&#xff09; 1、type1-LSA&#xff1a;----重要且复杂 2、type2-LSA&#xff1a; 3、type3-LSA&#xff1a; 4、type4-LSA&#xff1a; 5、type5-LSA&#xff1a; 6、type7-LSA&#xff1a; 三、OSPF的网络类…...

FPGA时序分析与约束(9)——主时钟约束

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

sqlite3 关系型数据库语言 SQL 语言

SQL(Structured Query Language)语言是一种结构化查询语言,是一个通用的,功能强大的关系型数据库操作语言. 包含 6 个部分: 1.数据查询语言(DQL:Data Query Language) 从数据库的二维表格中查询数据,保留字 SELECT 是 DQL 中用的最多的语句 2.数据操作语言(DML) 最主要的关…...

spring boot中的多环境配置

1.切换环境 spring:profiles:include: devactive: dev的作用是为了启动某个环境&#xff0c;两个作用基本一致&#xff0c; 环境定义如下&#xff1a; spring:profiles: dev或者是查找application-dev.yml这个文件的所有配置 2.加载文件 spring:config:import:- optional:f…...

python3 阿里云api进行巡检发送邮件

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

【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 搭建搜索结果页面总结 上一篇我们讲解了店铺信息的展示功能&#xff0c;本篇讲解一下搜索功能。通常小程序在首页都配置了搜索的功能&#xff0c;输入关键词进行检索&#xff0c;可以在结果页上进行选购。同时还记录了用户的搜…...

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

PHP自定义文件缓存实现

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

猫耳 Android 播放框架开发实践

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

linux下df -h 命令一直卡住的解决方法

在Linux中&#xff0c;偶尔遇到用 df -h 查看磁盘情况时&#xff0c;一直卡住无法显示结果。 解决方法&#xff1a; 1、首先使用strace追踪到底执行到哪里卡住 $ strace df -h 2、如果没有strace命令则进行安装 $ yum install strace -y 3、显示出卡住的地方&#xff0c;如…...

系统架构设计热点知识

系统架构设计师考点包括以下内容&#xff1a; 1. 系统设计和架构思想. 了解系统设计和架构的基本概念和思想&#xff0c;特别是面向服务架构&#xff08;SOA&#xff09;、微服务架构、云架构、事件驱动架构、响应式架构等。 系统设计是指在软件项目中&#xff0c;确定系统结…...

2023-在mac下安装Homebrew的国内镜像

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

Ubuntu 20.04设置虚拟内存 (交换内存swap)解决内存不足

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

RabbitMQ-死信交换机和死信队列

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

[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(__…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...