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(参数错误)

输出:

相关推荐
黄毛火烧雪下2 小时前
Java 核心知识点总结(一)
java·开发语言
其实防守也摸鱼2 小时前
软件安全与漏洞--软件安全编码与防御技术理论题库
开发语言·网络·安全·网络安全·软件安全·软件安全与漏洞
x138702859572 小时前
c语言中srtlen(指针使用计算字符长度)、传值和传址调用
c语言·开发语言·算法·visual studio
iCxhust2 小时前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
凡人叶枫2 小时前
Effective C++ 条款28:避免使用 handles 指向对象内部
linux·服务器·开发语言·c++·嵌入式开发
努力成为AK大王2 小时前
并发编程的核心挑战、优化方案与核心知识点总结
java·开发语言·数据库
zwenqiyu2 小时前
P5283 [十二省联考 2019] 异或粽子题解
c++·学习·算法
Queenie_Charlie2 小时前
哈夫曼树
数据结构·c++·哈夫曼树
AI 编程助手GPT3 小时前
用 Python 做一个世界杯赛前分析脚本:以巴西 vs 摩洛哥为例
开发语言·网络·人工智能·python·chatgpt