第10章 异常处理

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第十章异常处理

当发生运行时错误时,不能简单地结束程序运行,而是退回到任务的起点,指出错误,并由用户决定下一步工作。

函数执行时,放在try(测试)程序块中的任何类型的数据对象发生异常,都可被throw表达式抛出,随即逆调用链退回,直到被catch子句捕获,并在此执行异常处理,报告出现的异常等情况。

10.1 异常的概念

异常概念的引入:异常是程序可能检测到的,运行时不正常的情况,如存储空间耗尽、数组越界、被0除等等。

C++提供了一些内置的语言特性来产生或抛出异常,用以通知“异常已经发生”,然后由预先安排的程序段来捕获异常,并对它进行处理。这种机制可以在C++程序的两个无关(往往是独立开发)的部分进行“异常”通信。由程序某一部分引发了另一部分的异常,这一异常可回到引起异常的部分去处理(逆着程序函数的调用链)。

10.2 异常处理机制

异常与异常抛出:(以栈为例,异常类声明如下)

class popOnEmpty{...}; //栈空异常

class pushOnFull{...}; //栈满异常

测到栈满或空就抛出一个异常。

template void Stack::Push(const T&data){

if(IsFull()) throw pushOnFull(data);//注意加了括号,是构造一个无名对象

elements[++top]=data; }

templateT Stack::Pop(){

if(IsEmpty()) throw popOnEmpty();

return elements[top--]; }

注意pushOnFull是类,C++要求抛出的必须是对象,所以必须有“()”,即调用构造函数建立一个对象。

throw表达式抛出异常为异常处理的第一步。在堆栈的压栈和出栈操作中发生错误而抛出的异常,理所当然地应由调用堆栈的程序来处理。异常并非总是类对象,throw表达式也可以抛出任何类型的对象,如枚举、整数等等。但最常用的是类对象。

在C++中异常抛出与异常处理之间有一整套程序设计的机制。首先采用关键字try,构成一个try块(try block),它包含了抛出异常的语句。当然也可以是包含了这样的调用语句,该语句所调用的函数中有能够抛出异常的语句。

try块与catch子句的关系实例:

int main(){

int a[9]={1,2,3,4,5,6,7,8,9},b[9]={0},i;

stackistack(8);

try{ for(i=0;i<9;i++) istack.Push(a[i]);

istack.PrintStack(); }

catch(pushOnFull){cerr<<”栈满”<

try{ for(i=0;i<9;i++){b[i]=istack.Pop();} }

catch(popOnEmpty){cerr<<”栈空”<

for(i=0;i<9;i++) cout<

cout<

return 0; }

说明:

这里有两个try块,分别对应压栈与出栈;也有两个catch子句(catch clause),分别处理压栈时的栈满和出栈时的栈空。

由catch字句捕获并处理异常是第二步。注意与catch语句分别匹配的是在压栈和出栈成员函数模板中的throw语句,一个抛出pushOnFull类的无名对象,另一个抛出popOnEmpty 类的无名对象。

在编制程序时有一条惯例:把正常执行的程序与异常处理两部分分隔开来,这样使代码更易于跟随和维护。在上例中,我们可以把两个try块合成一个,而把两个catch子句都放在函数最后。

流程控制规则:

1.如果没有异常发生,继续执行try块中的代码,与try块相关联的catch子句被忽略,程序正常执行,main()返回0。

2.当第一个try块在for循环中抛出异常,则该for循环退出,try块也退出,去执行可处理pushOnFull异常的catch子句。istack.PrintStack()不再执行,被忽略。

3.如果第二个try块调用Pop()抛出异常,则退出for和try块,去执行可处理popOnEmpty 异常的catch子句。

4.当某条语句抛出异常时,跟在该语句后面的语句将被跳过。程序执行权交给处理异常的catch子句,如果没有catch子句能够处理异常,则交给C++标准库中定义的terminate()。

10.3 栈展开与异常捕获

catch子句说明:

当try块中的语句抛出异常时,系统通过查看跟在其后的catch子句列表,来查找可处理该异常的catch子句。

catch子句由三部分组成:关键字catch、圆括号中的异常声明以及复合语句中的一组语句。注意:catch子句不是函数,所以圆括号中不是形参,而是一个异常类型声明,可以是类型也可以是对象。

catch子句与函数的不同之处在于:它只有一个子句,没有定义和调用之分。使用时由系统按规则自动在catch子句列表中匹配。

catch子句可以包含返回语句(return),也可不包含返回语句。包含返回语句,则整个程序结束。而不包含返回语句,则执行catch列表之后的下一条语句。

catch子句异常声明探讨:异常声明中可以是一个对象声明。以栈为例。当栈满时,要求在异常对象中保存不能被压入到栈中的值。

catch子句的异常声明与函数参数声明类似,可以是按值传送,也可以是按引用传递。

使用引用类型的异常声明,catch子句能够修改异常对象,但仅仅是异常对象本身,正常程序部分的量并不会被修改。与一般类对象不同,实际上异常对象处理完后,生命期也就结束了。

函数try块的使用:

把程序的正常处理代码和异常处理代码分离的最清楚的方法是定义函数try块。这种方法是把整个函数包括在try块中。

一个函数try块把一组catch子句同一个函数体相关联。如果函数体中的语句抛出一个异常,则考虑跟在函数体后面的处理代码来处理该异常。函数try块对构造函数尤其有用。

相关文档
最新文档