我们首先来补充异常处理机制的内容
这个知识点其实相当重要
它涵盖了下面的几个主题
一个是异常处理机制的基本概念
第二个就是异常的引发
第三个就是异常的捕获
第四个就是异常类与异常对象
第五个是异常处理策略
最后一个是我们的异常描述规范
我们首先来看异常处理机制
它的基本概念
首先我们要搞清楚什么是异常
异常是什么呢 异常就是程序中可以检测的运行不正常的情况
它不正常 那就叫异常
你比如讲 被零除
数组下标越界存储空间不足
你想分配一个内存但结果存储空间不足
这个就叫异常情况
它不太正常嘛 对吧
所以就要处理这个问题
一个健壮的程序这些问题都必须被处理
早期我们是怎么写程序呢
我们可以写一个 if 判断它
当它出错的时候我们就做特定的处理
我们还可以写什么呢
我们还可以使用断言来处理这个问题
到了 C++ 里边
为了和面向对象类库的架构相兼容
它提供了异常处理机制
允许你定义异常类
给了你一个更灵活的处理异常的技术手段
异常处理的最基本的流程
首先 某段程序代码在程序的执行期间
发生了一个特殊的情况
也就是一个异常的情况
那么它就会引发这样的异常
第二 另一段程序代码就会捕获这个异常 并且处理它
这个是异常处理机制的最基本的流程
我们来看一个例子 这个例子是栈
我们定义了一个类 JuStack
栈这个概念 我们前面解释过
它是一种很特殊的数据结构
它的元素满足先进后出规则
我们定义的这个栈类里边
有一个私有的指向整数的指针 _stk
有三个整数成员 _cap、_cnt、_top
前面都有下滑线
它记录了我们栈保存的全部的数据
和这个栈的相应的管理信息
_cap 表示这个栈的容量
_cnt 表达的是这个栈当前的元素个数
_top 表示我们的栈顶指针
位于第几个元素下标处
_stk 就是指向那个数组的 0 号元的指针
我们构造这个栈的时候
要 new 一段存储空间出来
然后让 _stk 指向那个存储空间
一开始容量是传进来的
而栈的元素个数和栈顶指针都是 0 号元
我们提供了两个最重要的函数
一个是 Pop() 一个是 Push()
一个是把元素从栈中弹出来
一个是把元素压到栈里面去
剩下几个都是一些 Get/Set 函数
或者是判断函数
像 IsEmpty() 判断栈是不是空的
IsFull() 判断栈是不是满的
这个栈类实现是很简单的
我们看它的具体代码
但实际上它是有问题的
首先当你调用成员函数 Stack::Pop()
想弹出一个数据的时候
栈是空的 你怎么办
第二个 如果你调用成员函数 Stack::Push()
想把一个元素压栈的时候
如果栈是满的 你怎么办
这是这个栈类在实现的时候
必须处理的两个问题
那么在异常处理机制下
我们就可以为它设计这样的解决方案
定义一个异常类
然后修改它对应的成员函数
在出现异常情况的时候引发这个异常
并且在需要的位置处理这个异常
这是异常处理机制下
我们实现这个栈类的关键
所以我们可以把刚才那段代码做一些修正
首先我定义两个类：EStackEmpty、EStackFull
这是两个异常类
一个表示栈是满的异常
一个表示栈是空的异常
这两个类都是空类
里面什么东西都没写
没关系 我们要的就是这个类的名字
定义好这两个空类就够了
我们修正 Stack::Pop() 这个函数
当栈是空的时候
我们会 throw EStackEmpty()
throw 就是 C++ 为我们提供新的关键字
表示引发一个异常
当栈是空的时候
我们就会引发一个异常
EStackEmpty() 就表示
我要构造 EStackEmpty 这个类的一个匿名对象
然后把那个佚名对象抛出来
这就叫引发这个异常
类似地 Stack::Push()
当我把一个元素压栈的时候
如果这个栈是满的
我们就会抛出一个 EStackFull 异常
这是 Push() 和 Pop() 这两个函数实现时候所做的修改
在这个修改里边 我们就使用到了异常处理机制
我们看主程序 我们主函数main()
定义 JuStack 类的一个对象 stack
标准容量是 17 个元素
我们尝试着向里面压栈
把一些数据朝里面压栈
注意我的异常处理的机制
我使用 try ... catch 这样的结构来处理有可能导致异常的程序代码
也就是说 我们将有可能导致栈异常的代码封装在 try 这个块里
try 的花括号对里
for 循环 stack.Push()
做的就是这个事
因为 Push() 这个函数有可能导致异常
所以我就把它封装在 try 这个花括号对里
catch 就来捕获这个异常
当它发生一个异常的时候
我们 catch 这条子句就会捕获这个异常
然后去做对应的处理
看到我这里面的代码
std::cerr 就向标准错误流里边输出一个提示信息
这就是 try ... catch
我们的异常处理机制的最基本的方案
catch 就负责捕获那个异常
因为 Push() 在将元素压栈的时候
是有可能导致一个栈满异常的
所以我们的 catch 子句将捕获 EStackFull 这个类的一个异常对象
当然我们捕获的是对这个常对象的一个引用
后面没跟着它的参数 不需要写
因为我们不关心它那个参数具体是什么
内容是什么 我们现在不关心
所以不需要写 可以
这个就是异常的捕获