C++进阶篇7---异常

目录

一、异常的介绍

二、异常的使用 

1.异常的抛出和捕获

2.异常的重新抛出

3.异常安全

4.异常规范

三、自定义异常体系

四、C++标准库的异常体系


一、异常的介绍

异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者来处理这个错误
  • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
  • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常.catch 关键字用于捕获异常,可以有多个catch进行捕获。
  • try: try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块。
try
{// 保护的标识代码
}
catch (ExceptionName e1)
{// catch 块
}
catch (ExceptionName e2)
{// catch 块
}
catch (ExceptionName eN)
{// catch 块
}

当try中的代码出现异常时,会被catch捕获,并执行相关的处理语句

二、异常的使用 

1.异常的抛出和捕获

通过上图我们会发现,异常的捕获是严格按照类型的,如果类型不匹配,即使抛了异常也不会被处理,所以为了防止这种情况导致的程序崩溃,我们可以用catch(...)用来捕获任意类型的异常, 当然这里的类型匹配,也可以是父子类的关系,即子类可以被父类捕获(很实用)

通过上图,我们发现,throw语句如果执行,那么在它下面的语句就不会执行,它会直接跳到捕获了该异常的地方继续执行代码,且catch里的语句执行完之后会接着往后执行代码,而不会跳到抛出异常的地方往后执行 (这里注意,如果有多处地方都能捕获该异常,则该异常会被离他最近的catch捕获,符合就近原则)

总结:

异常的抛出和匹配原则
1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。
2. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)
4. catch(...)可以捕获任意类型的异常,问题是不知道异常错误是什么。
5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,这个在实际中非常实用。
在函数调用链中异常栈展开匹配原则
1. 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则调到catch的地方进行处理。
2. 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
3. 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(...)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。
4. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。

2.异常的重新抛出

在上诉场景中,由于异常的抛出可能导致某些释放资源的语句不能执行,所以需要在处理该异常之前,将其捕获,从而手动释放资源,再将异常抛出,交由相应的catch语句处理,当然这个不是主要的解决方案,一旦new的资源多了,代码量就会变大,具体的得看智能指针

3.异常安全

  • 构造函数完成对象的构造和初始化最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
  • 析构函数主要完成资源的清理最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)
  • C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题,关于RAII我们智能指针这节进行讲解。

4.异常规范

// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept;

 1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型。

2. 函数的后面接throw(),表示函数不抛异常。

3. 若无异常接口声明,则此函数可以抛掷任何类型的异常 
注意:这里的规范只是规范而已,也就是说throw()里写的异常,和没写的异常都有可能被抛出,编译器不做强制检查,但是C++11的noexcept一旦写了,就不允许在抛出异常了,否则会运行报错

三、自定义异常体系

这个其实在公司里用的比较多,因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了,所以实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。

四、C++标准库的异常体系

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

五、异常的优缺点

优点

1.异常能帮助我们更好的定位和解决bug,相比于错误码,它能够清晰的展示出各种错误信息

2.返回错误码的传统方式,需要一层一层返回这个错误码,也就是说每一层都需要针对错误码进行判断然后返回,而异常会直接跳到相应的catch语句

3.很多第三方库都包含异常,如boost、gtest、gmock等等常用的库

4.部分函数更适合用异常来处理,比如构造函数,析构函数这类没有返回值的函数,等等

缺点

1.异常会导致程序的运行不按照正常的顺序执行,不停的在各个函数之间跳跃,导致我们跟踪调试及分析程序时比较困难

2.异常会有一些性能开销

3.C++中申请的资源需要自己管理,有了异常会容易导致内存泄漏、死锁等问题,需要用智能指针处理管理资源问题

4.C++标准库的异常体系定义得不好,导致大家都选择使用自己定义的异常体系

总结:异常总体来说,利大于弊。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode437.路径总和III

看完题目我就拿直接用递归写了如下代码: class Solution {private int ans;public int pathSum(TreeNode root, int targetSum) {ans 0;dfs(root, targetSum, 0);return ans;}public void dfs(TreeNode root, int targetSum, int sum){if(root null)return;sum r…

CSS面经(未完待续)

1. CSS选择器及其优先级 !important > 行内样式 > id选择器 > 类/伪类/属性选择器 > 标签/伪元素选择器 > 子/后台选择器 > *通配符 2. 重排和重绘是什么?浏览器的渲染机制是什么? 重排(回流):当增加或删除dom节点&…

超大规模集成电路设计----CMOS反相器(五)

本文仅供学习,不作任何商业用途,严禁转载。绝大部分资料来自----数字集成电路——电路、系统与设计(第二版)及中国科学院段成华教授PPT 超大规模集成电路设计----CMOS反相器(五) 5.1 静态CMOS反相器综述5.1.1 静态CMOS反相器优点…

【C语言:自定义类型(结构体、位段、共用体、枚举)】

文章目录 1.结构体1.1什么是结构体1.2结构体类型声明1.3结构体变量的定义和初始化1.4结构体的访问 2.结构体对齐2.1如何对齐2.2为什么存在内存对齐? 3.结构体实现位段3.1什么是位段3.2位段的内存分配3.3位段的跨平台问题3.4位段的应用3.5位段使用注意事项 4.联合体4…

Chapter 6 Managing Application Engine Programs 管理应用程序引擎程序

Chapter 6 Managing Application Engine Programs 管理应用程序引擎程序 Running Application Engine Programs 运行应用程序引擎程序 This section provides an overview of program run options and discusses how to: 本节提供程序运行选项的概述,并讨论如何…

上下拉电阻会增强驱动能力吗?

最近看到一个关于上下拉电阻的问题,发现不少人认为上下拉电阻能够增强驱动能力。随后跟几个朋友讨论了一下,大家一致认为不存在上下拉电阻增强驱动能力这回事,因为除了OC输出这类特殊结构外,上下拉电阻就是负载,只会减…

RT-Thread Studio文件消失不见或被排除构建

不得不说RT-Thread Studio里面配置真多,今天我同事的电脑发现根本没有被画斜杠的文件夹,导致我想移植f1的写内部flash这个(可以看上一个文章)时候不能直接点击属性排除构建,然后在网上查找的时候也没怎么找到说法&…

代码随想录算法训练营 ---第五十五天

今天是 动态规划:编辑距离问题。 第一题: 简介: 动态规划五部曲: 1.确定dp数组的含义 dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。 2.确定递推公…

智能优化算法应用:基于寄生捕食算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于寄生捕食算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于寄生捕食算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.寄生捕食算法4.实验参数设定5.算法结果6.参考…

玩转Omniverse | 将FBX文件导入Omniverse View,以及step等3D格式如何转换为USD文件的过程

1,参考这个过程,玩转Omniverse | 将FBX文件导入Omniverse View 2,实际操作: 在omniverse中安装usd explorer 打开usd explorer 选择step,然后右键选择convert to USD,点击确认,稍等一会就会转换…

Python神器解析时间序列数据:数据分析者必读

更多资料获取 📚 个人网站:ipengtao.com 时间序列数据是在许多领域中都至关重要的数据类型,它涵盖了一系列按时间顺序排列的数据点。Python作为一种强大的数据分析工具,提供了许多库和工具,能够有效地处理、分析和可视…

高级搜索——伸展树Splay详解

文章目录 伸展树Splay伸展树Splay的定义局部性原理Splay的伸展操作逐层伸展双层伸展zig-zig/zag-zagzig-zag/zag-zigzig/zag双层伸展的效果与效率 伸展树的实现动态版本实现递增分配器节点定义Splay类及其接口定义伸展操作左单旋右单旋右左/左右双旋伸展 查找操作删除操作插入操…

springcloud整合Oauth2自定义登录/登出接口

我使用的是password模式&#xff0c;并配置了token模式 一、登录 (这里我使用的示例是用户名密码认证方式) 1. Oath2提供默认登录授权接口 org.springframework.security.oauth2.provider.endpoint.postAccess; Tokenpublic ResponseEntity<OAuth2AccessToken> pos…

hbase thrift2 jar包冲突导致启动失败问题排查记录

1、启动命令 ${HBASE_HOME}/bin/hbase-daemon.sh start thrift2 2、异常情况 hbase-root-thrift2-hdfs-test07.yingzi.com.out异常日志&#xff1a; Exception in thread "main" java.lang.AbstractMethodError: org.apache.hadoop.metrics2.sink.timeline.Hadoo…

Nginx(十二) gzip gzip_static sendfile directio aio 组合使用测试(2)

测试10&#xff1a;开启gzip、sendfile、aio、directio1m&#xff0c;关闭gzip_static&#xff0c;请求/index.js {"time_iso8601":"2023-11-30T17:20:5508:00","request_uri":"/index.js","status":"200","…

【Java Web学习笔记】4 - DOM文档对象模型

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/javascript 零、在线文档 JavaScript HTML DOM 一、HTML DOM基本介绍 1. DOM全称是Document Object Model文档对象模型 文档<---映射--->对象 2.就是把文档中的标签&#xff0c;属性&#xf…

WebSocket入门介绍及编程实战

HTTP的限制 全双工和半双工&#xff1a; 全双工&#xff1a;全双工&#xff08;Full Duplex&#xff09;是允许数据在两个方向上同时传输。 半双工&#xff1a;半双工&#xff08;Half Duplex&#xff09;是允许数据在两个方向上传输&#xff0c;但是同一个时间段内只允许一个…

解决 引element-plus依赖时的core-js报错

参考资料&#xff1a; https://blog.csdn.net/weixin_42164539/article/details/123388542 本人正在重构两年前搭建到一半的博客网站&#xff0c;相关依赖都很陈旧&#xff0c;用到了 npm-check-updates 检测项目可升级依赖&#xff1a; 补依赖过程始中报错 解决方案&#xf…

linux 内核同步互斥技术之信号量

信号量 信号量允许多个进程同时进入临界区&#xff0c;大多数情况下只允许一个进程进入临界区&#xff0c;把信号量的计数值设置为 1&#xff0c;即二值信号量&#xff0c;这种信号量称为互斥信号量。可允许多个锁持有者。 和自旋锁相比&#xff0c;信号量适合保护比较长的临界…

Java-宋红康-(P133-P134)-多线程创建方式(Thread and Runnable)

b站视频 133-多线程-线程创建方式1&#xff1a;继承Thread类_哔哩哔哩_bilibili 目录 3.1 继承Thread 3.1.1 继承Thread类方式 3.1.2 线程的执行流程 3.1.3 线程内存图 3.1.4 run()方法和start()方法 3.1.5 线程名字的设置和获取 3.1.6 获取运行main方法线程的名字 3.…