大家好
欢迎继续学习C++语言程序设计
这一节我们来学习
异常处理的思想和程序实现
首先呢
我们通过一个图
让大家来了解一下
异常处理的基本思想
从这张图我们可以看到
在引发异常的模块中呢
有可能我们是不处理异常的
也就是说
某一个函数h
在它运行期间
可能发生了环境
或者用户使用的故障
那么这个时候呢
导致计算不能够继续下去了
但是函数h
它自己可以并不去处理这个异常
谁调用了它呢
它就将这个异常
抛送给它的调用者
比如说函数g
可能是函数h的调用者
函数g接收到抛出的异常以后呢
它可以选择处理这个异常
或者不处理
继续向它的调用者再次抛出
那么异常有可能
就沿着调用栈一级一级向上抛出
直到某一级函数
它的分工就是捕获
并且处理这些异常
那么它就把这些异常问题
在这里处理
这样的话呢
我们就看到
实际上这个异常的抛出方向
和函数的调用方向
正好是反过来的
所以异常的抛出
都是向它的上级调用者去抛出
那么这就是异常处理的基本思想
当程序运行过程中
发现用户使用行为不当
或者环境因素造成
计算无法继续的时候
那么C++
给我们提供的异常处理机制
可以让程序具有容错功能
这个容错功能
又不用干扰程序执行的主逻辑
不是说在发生异常的当地
总是立即处理异常
而是可以选择你处理也行
不处理
直接向上抛给调用者也行
那么调用者同样可以继续上抛
这样我们就可以
将不同的模块进行分工
让它们有分工 有合作
有的模块专注于执行
主要的计算逻辑
而有的模块呢
专门用来做各种各样的容错处理
现在我们来看一下
异常处理的语法
在程序段中
如果遇到有异常呢
可以通过throw
创建一个异常对象
并且抛出
那么抛出以后
怎么能够使得
该处理异常的程序段
能够得到这个异常信息呢
我们就需要一个抛出
和捕获配合的这样一个机制
也就是说
我们需要
将可能抛出异常的程序段
放在这个try块里面
那么通过正常的顺序
执行到try语句以后
会执行try块里面的内容
我们叫保护段
如果在保护段执行期间
没有引发异常
因为异常不是说一定要出现的
异常是非常偶然的情况
它会偶然出现
那么大多数情况下
我们执行
这个try保护段里面的内容
都不会引起异常
那么这个时候
跟在try块后面的catch子句
就不会被执行
程序会从try块以后的
最后一个catch之后的语句
继续执行
catch子句呢
会按照它在try块后
出现的顺序依次被检查
当try块里面有异常抛出的时候
那么try块后面
属于列出了多个catch块
它就从前往后依次检查
抛出的异常对象
跟哪个catch子句里面的
异常声明类型匹配了
那么就进入哪个catch子句
里面的catch块
这个catch块里面的内容呢
往往是进行异常处理
如果说所有catch中的异常声明
都没有匹配上
也就是匹配的处理器没有找到
那么库函数terminate
就会被自动调用
那么它默认的功能
就是调用abort终止这个程序
下面我们来看一个例子
在这个例子中呢
通过异常处理机制
来处理除0异常
这里我们将这个除法运算
交给函数divide来完成
传给它两个参数x y
希望它去计算x除以y
首先它就要判断
这个除数是否是0
如果除数是0
就不能做这个除法
直接将第一个参数x
作为异常对象抛出
当然了抛出异常对象
可以是一个我们为了
这种异常情况
构造的一个包含很多信息的对象
也可以
就是一个基本数据类型的变量
现在就将x这个被除数本身
作为异常对象抛出了
你抛出什么类型的数据
作为异常对象都可以
只要抛出和接收约定好就行
如果除数不为0的话
就计算x除以y
好 现在看主函数中
既然这个divide函数
是有可能抛出异常的
所以我们将三次对divide的调用
都放在try块里面
后面这个catch块就来捕获异常
注意
这里捕获的是一个int类型的
异常对象
这跟我们这个divide
抛出的异常类型
正好是约定好的了
它要抛出
也是抛出一个int作为异常对象
那么这儿catch当然就是匹配
接收一个int作为异常对象
第一次调用它的时候
给的参数是5和2
计算5除以2是没有问题的
大家看这个结果反馈回来了
第二次呢
调用的时候给的参数是8和0
也就是这意思是要计算8除以0
当然这个divide函数一判断
除数为0
它就不会做这个除法
就直接将第一个参数8
作为异常对象抛出了
所以
后面这个除法就没有计算
抛出了以后
这个try块后面的运算
7除以1也就不会再执行了
try块后面所有的东西都停止
不执行了
直接就到catch子句这儿来
去捕获
正好捕获的也是一个整数
于是就把这个异常对象捕获到了
然后就将这个信息输出
e被0除了
我们看到输出的是8
被0除
然后That is ok
程序就此结束
所以我们看到
这是一个典型的
也就是特别简单的
遇到了异常
不再就地处理
直接抛出给它的调用者
它的调用者呢
将有可能抛出异常的代码
都放在try块里面
后面用catch去捕获这个异常
这是一个典型的异常处理机制
如果一个函数
它知道自己有可能抛出
哪些类型的异常
最好就在编写函数的时候
声明一下
我这个函数是预期有可能抛出
什么什么类型异常的
这样显示声明以后
程序的可读性就比较好
这样这个函数的调用者
就能够知道
它调用的函数
是有可能抛出哪些异常的
这样在调用者函数中呢
就比较方便
为处理这些异常做好准备
我们看
这就是异常接口声明机制
所谓异常接口声明
我们看它的语法就是这样的
在函数声明的时候
加一个throw
throw后面这一对括号里面
就列出这个函数
它运行时有可能抛出的异常类型
打算抛出多少个类型的异常
就列多少个类型在这儿
那么如果一个函数
它没有进行异常接口声明
那恰恰说明这个函数
它有可能抛出任何类型的异常
那它都可能
抛出任何类型的异常了
它的调用者就很难专门针对性地
为某种异常去做好准备了
所以呢
建议大家写函数的时候
如果它有可能抛出异常
你一定列出它打算抛出什么异常
如果一个函数
肯定不会抛出异常的
我们也可以去
写一个throw
后面一对空括号
那就表示这个函数
不会抛出任何异常