C++进阶 异常

一.异常的概念及使用

1.异常的概念

异常就是:

程序运行过程中发生了"不正常情况",程序通过一种特殊机制,把错误信息传递出去,再由其他地方统一处理。

例如:

  • 除数为 0
  • 文件打开失败
  • 内存申请失败
  • 数组越界
  • 网络连接失败

这些都属于异常情况。

没有异常时(传统方式)

比如:

调用:

这里的问题:

问题1:返回值可能冲突

比如:

结果也是:-1

到底是正常结果还是错误?

无法区分。

问题2:错误信息不够

返回:-1

无法知道:

  • 哪个函数错了
  • 为什么错
  • 错误位置
  • 怎么修复

只能查错误码。

C语言没有异常机制。

通常这样:

errno

系统错误编号。

例如:

2.异常的抛出和捕获

例如:

输出:捕获异常:10

throw执行后,后面的代码不会执行

例如:

输出:1

异常

当前调用链决定哪个catch处理异常

意思:

程序从当前函数开始向外层找。

例如:

输出:100

这叫异常传播

离throw最近且类型匹配的catch处理

例子:

输出:内部

如果内部类型不匹配:

继续向外传播

最终:外部

3.栈展开

正常函数调用:

异常发生:

程序不再正常返回。

当异常抛出后:
先检查当前函数有没有try

先看当前函数内部能不能处理。

例如:

输出:当前函数处理

当前函数没有catch

退出当前函数继续查找

调用:

发生:throw

查找:

输出:处理异常

这个过程就叫:

栈展开

沿调用栈不断退出。

退出函数时对象会析构 ,调用链创建对象都会销毁

输出:析构

捕获

异常退出也会释放资源。

类型不匹配继续向外找

如果:

则全部匹配

到main还没找到会调用标准库终止程序

例子:

输出可能:

找到catch以后程序继续执行

输出:处理

继续执行

继续的是:整个try-catch后面,不是回到 throw

4.查找匹配的处理代码

1.最常见情况:类型完全匹配

throw 什么类型,就用什么类型接。

如果不一致,继续向外传播。

2.有多个catch,选最近且匹配的

3.允许非const → const(权限缩小)

输出:成功

但是反方向不行:

因为权限放大了

4.数组→指针转换

数组允许退化成指针

输出:hello

发生数组退化

5.函数→函数指针转换

6.派生类异常可以用基类捕获

抛:

能捕获。

完整例子:

输出:除0

工程里:

统一:

处理所有异常。

这是标准写法。

7.catch(...) 捕获所有异常

一般main最后加catch(...)

匹配任意类型

输出:未知异常

但:

只能知道发生异常,不知道具体是什么异常

5.异常重新抛出

当前函数可能只能处理一部分异常,剩下的交给更外层统一处理。

语法:

这里没有对象。

表示:把当前正在处理的那个异常继续向外扔。

例子:

输出:内部处理

外部处理

为什么必须写 throw; 而不是 throw e;

表示:新抛出一个 e 的副本。

可能发生:

  • 再拷贝一次
  • 类型退化(对象切片)

结果:

叫:对象切片

正确:

表示:继续抛原来的异常对象

不会复制。

不会降级

重新抛出时会重新找catch

输出:最终处理

重新抛出时栈展开仍然发生

资源照样释放

6. 异常规范

一个函数会不会抛异常,能不能提前告诉调用者?

异常规范就是干这个的。

它本质上是一种:函数的异常承诺

告诉别人:

  • 我不会抛异常
  • 我可能抛哪些异常
  • 调用我需不需要准备 catch

假设有函数:

调用者看到:

不知道

会不会抛异常?

抛什么异常?

要不要try?

于是有异常规范,提前说明;

例如:

表示:

保证不抛异常

C++98 的异常规范

语法:

函数后面:

1.throw()

表示:不会抛异常

表示:

这个函数保证不抛异常

如果违反:

程序结束

2.throw(type1,type2...)

表示:允许抛这些类型。

表示:

允许:

为什么淘汰?

因为太麻烦。维护困难

C++11 用 noexcept 简化

表示:保证不抛异常

调用者知道安全,不会异常

编译器不会严格检查 noexcept

可能编译通过

但运行:

结果:

程序结束

noexcept 还能作为运算符

作用:

检查表达式是否可能异常。

返回:

输出:1

说明:

不会抛。

析构函数默认就是 noexcept

默认等价:

所以:

通常:

二.标准库的异常

标准库没有把异常设计成杂乱的 throw intthrow "error",而是设计了一套继承体系,统一用基类 std::exception 管理。

1.标准库异常继承结构

其中:

exception(根基类)

所有标准异常父类。

定义类似:

关键:

支持多态

2.what() 是干什么的

返回错误描述。

例如:

输出:数据库连接失败

发生动态绑定

实际上调用:

不是:

3.几个常见标准异常

bad_alloc(内存申请失败)

输出类似:

out_of_range(越界)

输出:

invalid_argument(参数错误)

输出:

相关推荐
cui_ruicheng8 分钟前
Python数据分析(一):数据分析概述与环境搭建
开发语言·python·数据分析
心平气和量大福大26 分钟前
C#-WPF-UserControl-生命周期(加载 退出)
开发语言·c#·wpf
小龙报31 分钟前
【优选算法】1. 水果成蓝 2.找到字符串中所有字母的异位词
java·c语言·数据结构·数据库·c++·redis·算法
sunywz39 分钟前
【c#】 Web Deploy一键发布,IIS部署全流程
开发语言·前端·c#
Brilliantwxx1 小时前
【Linux】 软件包管理器(yum)+ Vim使用
linux·运维·服务器·开发语言·编辑器·vim
雪的季节1 小时前
Python 线程同步与异步编程 全解析
java·开发语言
txzrxz1 小时前
数论:排列数、组合数、费马小定理、逆元、同余定理
c++·算法·数论·组合数·费马小定理·逆元·排列数
520拼好饭被践踏1 小时前
JAVA+Agent学习day22
java·开发语言·后端·学习
笨蛋不要掉眼泪2 小时前
Java虚拟机:堆的参数配置
java·开发语言·jvm
冰暮流星2 小时前
javascript之date对象
开发语言·javascript·ecmascript