当前课程知识点:C++语言程序设计进阶 > 第十二章 异常处理 > 异常处理的思想与程序实现 > 异常处理的思想与程序实现
//12_1.cpp #include <iostream> using namespace std; int divide(int x, int y) { if (y == 0) throw x; return x / y; } int main() { try { cout << "5 / 2 = " << divide(5, 2) << endl; cout << "8 / 0 = " << divide(8, 0) << endl; cout << "7 / 1 = " << divide(7, 1) << endl; } catch (int e) { cout << e << " is divided by zero!" << endl; } cout << "That is ok." << endl; return 0; }
一个函数显式声明可能抛出的异常,有利于函数的调用者为异常处理做好准备
可以在函数的声明中列出这个函数可能抛掷的所有异常类型。
例如:void fun() throw(A,B,C,D);
若无异常接口声明,则此函数可以抛掷任何类型的异常。
不抛掷任何类型异常的函数声明如下:
void fun() throw();
大家好
欢迎继续学习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
后面一对空括号
那就表示这个函数
不会抛出任何异常
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义