C++的异常

在这里插入图片描述

文章目录

  • 1. C语言传统的处理错误的方式
  • 2. C++异常概念
  • 3. 异常的使用
    • 3.1 异常的抛出和匹配原则
  • 4. C++标准库的异常体系
  • 5. 自定义异常体系
  • 6. 异常的重新抛出
  • 7. 函数调用链中异常栈展开匹配原则
  • 8. 异常安全
  • 9. 异常规范
  • 10. 异常的优缺点

1. C语言传统的处理错误的方式

传统的错误处理机制:
1. 终止程序,如assert,缺陷:用户难以接受。如发生内存错误,除0错误时就会终止程序

2. 返回错误码,缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通过把错误码放到errno中,表示错误

实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。

2. C++异常概念

异常是一种处理错误的方式:当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误

throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的

catch: 在您想要处理问题的地方,通过异常处理程序捕获异常 catch 关键字用于捕获异常,可以有多个catch进行捕获

try: try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块。

3. 异常的使用

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。

举个例子:
在这里插入图片描述
这里是输入两个整数,进行相除,如果除数是0的话,就会出现问题。不过我们这里进行了捕捉,如果不捕捉程序就会终止。
在这里插入图片描述
这里throw后面可以跟任意类型的对象。

3.1 异常的抛出和匹配原则

1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码
在这里插入图片描述
像这个有多个catch的,还是会找对象类型最匹配的那个,也就是第一个。

2. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个
在这里插入图片描述
这里有两个try,它会走func里面的catch,main里面的catch就不会去了。

3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)

4. catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么。
如果某些人在抛异常的时候,抛了一个大家都不知道的类型,此时没有匹配的catch,我们就可以使用catch(…)来放在程序终止。
在这里插入图片描述
5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,这个在实际中非常实用。

4. C++标准库的异常体系

在这里插入图片描述
这是标准异常的基类。标准库组件抛出的所有对象都派生自此类。因此,可以通过引用捕获此类型来捕获所有标准异常。
在这里插入图片描述
这里的析构函数和what函数都被定义成了虚函数。what函数的作用是:返回错误信息。

C++ 提供了一系列标准的异常,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的:
在这里插入图片描述
在这里插入图片描述

举个例子:
在这里插入图片描述
这是分配内存失败时引发异常。
在这里插入图片描述
我们可以用这个派生类的类型来捕捉,也可以使用它的基类来捕捉。

在这里插入图片描述

5. 自定义异常体系

实际中我们可以去继承exception类实现自己的异常类。但是实际中很多地方是我们自己定义一套异常继承体系。因为C++标准库设计的不够好用。

举个例子:
在这里插入图片描述
我们可以自己定义一个异常类。里面有打印错误信息和错误码的函数。下面我们就可以自己去定义一些在某些情况下出现异常时可以返回错误信息:
在这里插入图片描述
这里写了一个网络服务的异常信息,它继承了上面的Exception类。它的构造函数可以添加一些参数,这样就能更加清楚是什么样子的错误。并且这个打印错误信息的函数是一个虚函数,这样可以重写。
在这里插入图片描述
这里就相当于一个网络服务的函数,因为在这里不好模拟出真正的网络错误,所有就用概率来表示。模10的意思是:只有10分之一的概率会触发这些错误。如果都没有,网络就成功。
在这里插入图片描述
这里捕捉一般我们只捕捉三个,一个是自己定义的,一个是标准库的(因为我们会用标准库里面的函数),一个未知异常。并且这里还使用了多态的概念。

运行结果如下:
在这里插入图片描述
这样在catch的时候就会打印错误信息。

如果在某些时候,我们想发送一条信息,因为网络问题,需要多次发送,我们该这么设置呢
在这里插入图片描述
这是一个发送服务,有网络错误和权限错误,错误码可以设置成2和1。现在的要求是:权限错误就直接报错,发送出现网络错误,要求重试10次
在这里插入图片描述
如果是网络错误,那么就continue上面的循环继续去发送信息。

那么现在有什么问题呢
如果是权限不足的错误,也break了。但是这个异常没有办法catch。
在这里插入图片描述
我们可以重新抛出异常,让下面的catch去捕捉。

运行结果如下:
在这里插入图片描述

6. 异常的重新抛出

有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理。
在这里插入图片描述
这里会出现一个问题:如果被除数是一个0,那么进入函数里会抛异常。抛异常就会跳转到catch然后被catch捕捉,那么delete就没有被执行,所有内存没有得到释放。
运行结果:
在这里插入图片描述
解决方法:
在这里插入图片描述
我们在func函数里加一个try和catch,这样当遇到throw的时候,就会被catch(…)捕捉,然后去释放掉内存,然后把遇到的异常再次抛出,让上一层再去捕捉一次。

运行结果:
在这里插入图片描述

7. 函数调用链中异常栈展开匹配原则

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

举个例子:
在这里插入图片描述

8. 异常安全

构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化

析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)

C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题,关于RAII会在智能指针这节进行讲解

9. 异常规范

1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型
在这里插入图片描述
2. 函数的后面接throw(),表示函数不抛异常
在这里插入图片描述
3. 若无异常接口声明,则此函数可以抛掷任何类型的异常
上面的是C++98期望我们去遵守这样的规则,声明清楚是否抛异常,抛什么异常。但是这是一种期望,但是并不是必须的,大家基本不会去遵守。
在这里插入图片描述
在C++11中前面的还可以使用,新增了一个noexcept。

10. 异常的优缺点

C++异常的优点
1. 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug

2. 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么我们得层层返回错误,最外层才能拿到错误,如果是异常体系,都不用检查,因为抛出的异常会直接跳到catch捕获的地方,直接处理错误

3. 很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常

4. 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理。比如T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误

C++异常的缺点
1. 异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难

2. 异常会有一些性能的开销。当然在现代硬件速度很快的情况下,这个影响基本忽略不计

3. C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题

4. C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱

5. 异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:一、抛出异常类型都继承自一个基类。二、函数是否抛异常、抛什么异常,都使用 func() throw();的方式规范化

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【LPC55s69】使用FAL分区管理与easyflash变量管理

文章目录 1.FAL组件1.1什么是FAL1.2 使用ENV配置FAL1.3 FAL SFUD 移植1.4 FAL SFUD 测试用例1.5 测试结果 2.DFS文件系统2.1 什么是DFS2.2 DFS架构2.3 使用ENV配置DFS2.4 DFS挂载到FAL分区测试2.5 测试结果 3.Easyflash移植到FAL分区3.1 简述EasyFlash3.2EasyFlash软件包使用3.…

windows 文件夹目录过长超过长度259字符 文件打不开

当路径超过260个字符时,Windows操作系统就无法处理文件或文件夹,并且无法打开或重命名。这是因为Windows系统使用的文件系统,即FAT和NTFS文件系统,都有最大路径限制。NTFS文件系统最大长度限制为32767个字符,但操作系统…

凝心聚力,携“源”出海:开源社顾问委员会2023年第一季度会议圆满举办!

2023 年 3 月 25 日,开源社顾问委员会(以下简称"顾问委员会")第一季度会议在北京圆满召开。这是顾问委员会自 2018 年成立以来的第 17 次全体委员会议。 为增进顾问委员会成员交流,加强开源社社区建设,实现开…

ansible自动运维——ansible使用临时命令通过模块来执行任务

大家好,这里是天亮之前ict,本人网络工程大三在读小学生,拥有锐捷的ie和红帽的ce认证。每天更新一个linux进阶的小知识,希望能提高自己的技术的同时,也可以帮助到大家 另外其它专栏请关注: 锐捷数通实验&…

制冷暖通工业互联网平台建设

制冷暖通工业互联网平台建设需要遵循一定的流程,具体步骤如下: 需求分析:了解客户需求,包括业务流程、系统功能、界面设计等方面。 系统设计:基于需求分析,进行系统设计,包括系统结构、数据库设…

jekyll+GithubPage搭建一个免费的个人网站

文章目录 Jekyll环境搭建windows安装RUBY、gem、Jekyll用Jekyll搭建本地博客 用jekyll模板搭建githubpage Jekyll环境搭建 windows安装RUBY、gem、Jekyll 安装ruby RUBY安装包下载地址:https://rubyinstaller.org/downloads/,一路默认选项next即可。 最…

LightGBM

LightGBM LightGBM是XGBoost的优化。 提出算法的原因: GBDT在每一次迭代的时候,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。尤其面…

python 第一章——简介与环境搭建

文章目录 前言一、什么是编程语言二、下载python解释器三、安装pycharm 前言 本系列教程目录可点击这里查看:python教程目录 python在当今世界的流行度应该已经不用我多说了,这可以从TIOBE的榜单中可窥一二 作为一门面向编程新人的语言,它…

matlab数据归一化与反归一化处理

假如数据实际取值范围为 X i ∈ [ − π π ] , i 1 , 2 , 3 X_i \in [-\pi \ \ \pi], i1,2,3 Xi​∈[−π π],i1,2,3,变量服从正态分布 示例如下: %% 数据归一化处理及其概率密度函数 clear clc Mu [0 0 0]; % 均值 Sigma [1 1 1]; % 标准差 C…

水羊转债,超达转债,晓鸣转债上市价格预测

水羊转债 基本信息 转债名称:水羊转债,评级:A,发行规模:6.94987亿元。 正股名称:水羊股份,今日收盘价:13.94元,转股价格:13.71元。 当前转股价值 转债面值 /…

【Leetcode每日一刷】动态规划:509. 斐波那契数、322. 零钱兑换、300. 最长递增子序列

博主简介:努力学习的22级计科生博主主页: 是瑶瑶子啦所属专栏: LeetCode每日一题–进击大厂 前言:动规五部曲 以下是《代码随想录》作者总结的动规五部曲 确定dp数组(dp table)以及下标的含义确定递推公式&#xff0…

ChatGPT写小论文

ChatGPT写小论文 只是个人对写小论文心得?从知乎,知网自己总结的,有问题,可以留个言我改一下 文章目录 ChatGPT写小论文-1.写论文模仿实战(狗头)0.论文组成1.好论文前提:2.标题3.摘要4.关键词5.概述6.实验数据、公式或者设计7.结论,思考8.参考文献 0.模仿1.喂大纲…

【轴承故障检测】滚动轴承中进行基于振动的故障诊断研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

学习笔记-主成分分析法

定义 主成分分析是一种降维算法,它能将多个指标转换为少数几个主成分,这些主成分是原始变量的线性组合,且彼此之间互不相关,其能反映出原始数据的大部分信息。一般来说,当研究的问题涉及到多变量且变量之间存…

人机交互有哪些SCI期刊推荐? - 易智编译EaseEditing

以下是几个人机交互领域的SCI期刊推荐: ACM Transactions on Computer-Human Interaction (ACM TOCHI): 由ACM(Association for Computing Machinery)出版的人机交互领域的顶级期刊之一,发表关于计算机和人之间相互作…

如何以产品经理思维打造一所高品质学校?

学校的建设与管理真不是一件容易事。2023年03月17日,山东菏泽市曹县一家长投诉某中学课业繁重,孩子经常写作业到半夜;2023年4月4日,张先生在华龙网重庆网络问政平台投诉万州区某中学伙食差,指出“发灰的洋葱&#xff0…

【21】核心易中期刊推荐——人工智能 | 遥感图像识别

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

023:Mapbox GL加载mp4视频文件

第023个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载MP4视频文件。一个视频源。 “urls”值是一个数组。 对于数组中的每个 URL,将创建一个视频元素源。 要支持跨浏览器的视频,请提供多种格式的 URL。“坐标”数组包含按顺时针顺序列出的视频角的 [longi…

如何在 Java 8 中使用 Streams?结合多种案例剖析学习!

Java 8 Streams 是一个非常强大的功能,它提供了一种简洁、优雅的方式来处理数据集合。通过使用 Streams,我们可以轻松地过滤、映射、排序、聚合等操作数据。本教程将介绍 Streams 的基本概念,以及如何在 Java 8 中使用 Streams。本教程还包括…

LinkedHashMap如何实现LRU缓存淘汰策略?

本文目录 1.LRU是什么?2.如何使用LinkedHashMap实现LRU?3.LinkedHashMap源码分析3.1 LinkedHashMap简介3.2 继承体系3.3 内部数据存储结构3.4源码解析属性:构造方法:afterNodeInsertion(boolean evict)方法afterNodeAccess(Node e)方法after…