第十五章C之异常处理.ppt

上传人:本田雅阁 文档编号:3128723 上传时间:2019-07-14 格式:PPT 页数:63 大小:682.02KB
返回 下载 相关 举报
第十五章C之异常处理.ppt_第1页
第1页 / 共63页
第十五章C之异常处理.ppt_第2页
第2页 / 共63页
第十五章C之异常处理.ppt_第3页
第3页 / 共63页
第十五章C之异常处理.ppt_第4页
第4页 / 共63页
第十五章C之异常处理.ppt_第5页
第5页 / 共63页
点击查看更多>>
资源描述

《第十五章C之异常处理.ppt》由会员分享,可在线阅读,更多相关《第十五章C之异常处理.ppt(63页珍藏版)》请在三一文库上搜索。

1、第十五章 异常 Chapter 15 Exception,异常是一种程序控制机制,与函数机制独立和互补 函数是一种以栈结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它依附于栈结构,却可以同时设置多个异常类型作为网捕条件,从而以类型匹配在栈机制中跳跃回馈. 异常设计目的: 栈机制是一种高度节律性控制机制,面向对象编程却要求对象之间有方向、有目的的控制传动,从一开始,异常就是冲着改变程序控制结构,以适应面向对象程序更有效地工作这个主题,而不是仅为了进行错误处理。 异常设计出来之后,却发现在错误处理方面获得了最大的好处,第十五章内容,错误处理的复杂性(Error Processing

2、Complexity) 使用异常(Using Exception) 捕捉异常(Catching Exception) 异常申述(Exception Description) 异常继承体系(Exception Inheritance System) 异常应用 (Exception Applications) 非错误处理(Non-Error Processing),15.1.1 错误种类,C+语言是按函数调用机制展开程序执行的,一般对处理错误的编程有下列常规手段: 1.遇到错误,立即终止程序运行。如打不开文件,或者读不到所要求的数据,则只能终止运行:,f1501.cpp,#include #inc

3、lude #include using namespace std; void fn() ifstream in(“abc.txt“); if(!in) couta; coutsqrt(a*1.0)“n“) if(a0) cout“Error: read in illegle data.n“; exit(1); /- int main() fn(); ,2.返回一个表示错误的值给上层函数 最常见的是逻辑值返回函数,如判断是否素数,判断是否字母等,对于不同要求的程序来说,很难说一定是错误的。 除此外,函数返回类型是一定的,所以若有些值是错误的,有些值是对的,必须预先规定一个范围,让调用者去判断。

4、应该让主体函数去处理问题。,3.返回一个合法值,但通过全局沟通手段,设置错误状态。 这种方式是为了远距离传输错误信息而采用的一种以破坏程序结构为代价的编程手法。如在C中,设置一个全局变量errno,专门存放错误代码,调用一个库函数后,为了确认其运行的正确性,便可以通过errno查访实现。,4.调用预先准备好的错误处理函数,让它决定是停止运行还是继续往下。,15.1.2 模块的隔绝性,可以将程序运行看作是诸多模块的工作及相互往来 一个模块发生的错误,需要借助于另一个模块进行修复 每个模块包含一个层层调用的函数体系 发生错误后,如果层层函数返回,则不但动作缓慢,而且错误信息丢失,失去处理的针对性

5、发生错误,说明模块服务失败,需要在主体模块中找说法,给予必要的处理. 不同的系统,其主体模块是不同的,决定了“说法”也是不同的如图:,15.1.3 调用链的牵制,函数是栈式管理的,从一个被调用的函数现场要把信息传递到相隔若干调用的主调函数,需要逐层退栈,一边退栈,一边通过函数返回值回馈,而且,每个函数都需要相应的接受判断以及接力传递的工作。如有一个简单的系列文件处理程序,假定文件在打开前,要经历文件名确定和打开方式确定的工作,如图15-2:,图15-2 链式调用与出错后跳跃返回,主函数,文件名 处理,打开方 式处理,打开输 入文件,打开输 出文件,出错跳回,f1502.cpp,#include

6、 #include using namespace std; void procFileName(string s); bool procOpenMode(string s); bool openIn(string s); bool openOut(string s); int main() procFileName(“iabc“); procFileName(“oabc“); void procFileName(string s) for(char c=0; c=9; c+) string t = s + c + “.txt“; if(!procOpenMode(t) cout“error

7、opening “t“n“; return;,bool procOpenMode(string s) if(s0=i ,15.2 使用异常,异常是专门针对抽象编程中的一系列错误处理的,C+中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试,如图:,错误处理示意:,放弃一棵子树,循调用链跳到祖先函数,发现错误处,异常超脱于函数机制,决定了其对函数的跨越式回跳。,15.2.1 异常使用三部曲,1.框定异常(try 语句块) 在祖先函数处,框定可能产生错误的语句序列,它是异常的根据,若不框定异常,则没有异常。 2.

8、定义异常处理(catch 语句块) 将出现异常后的处理过程放在catch块中,以便当异常被抛出,因类型匹配而捕捉时,就处理之。 3.抛掷异常(throw 语句) 在可能产生异常的语句中进行错误检测,有错就抛掷异常,19,异常处理的实现机制,抛掷异常的程序段 void Fun() throw 表达式; ,捕获并处理异常的程序段 try 复合语句 catch(异常类型声明) 复合语句 catch(类型 (形参) 复合语句 ,20,例12-1处理除零异常,#include int Div(int x,int y); int main() try cout“5/2=“Div(5,2)endl; cou

9、t“8/0=“Div(8,0)endl; cout“7/1=“Div(7,1)endl; catch(int) cout“except of deviding zero.n“; cout“that is ok.n“; int Div(int x,int y) if(y=0) throw y; return x/y; ,程序运行结果如下: 5/2=2 except of deviding zero. that is ok.,前两个步骤是一个函数中定义的,而抛掷异常则可以跨函数使用。 当直接在try语句块中使用时,异常处理退化为一般的错误处理模式。 在try语句块中,会有一些语句调用了其他函数,它

10、们之间则构成一个调用链,在调用链中的某一个结点上,如果出现抛掷语句,则便是一般意义上的异常了。,22,异常处理的基本思想,23,异常处理的实现机制(续),1 若有异常则通过throw操作创建一个异常对象并抛掷。 2 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。 3 如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。 4 catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。 5 如果匹配的

11、处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。,24,异常接口声明,可以在函数的声明中列出这个函数可能抛掷的所有异常类型。 例如: void fun() throw(A,B,C,D); 若无异常接口声明,则此函数可以抛掷任何类型的异常。 不抛掷任何类型异常的函数声明如下: void fun() throw();,25,异常处理中的构造与析构,找到一个匹配的catch异常处理后 初始化参数。 将从对应的try块开始到异常被抛掷处之间构造(且尚未析构)的所有自动对象进行析构。 从最后一个catch处理之后开始恢复执行。,26,例12-2 异常处理时的析

12、构,#include void MyFunc( void ); class Expt public: Expt(); Expt(); const char *ShowReason() const return “Expt类异常。“; ; void MyFunc() Demo D; cout“在MyFunc()中抛掷Expt类异常。“endl; throw Expt(); ,class Demo public: Demo(); Demo(); ; Demo:Demo() cout“构造 Demo.“endl; Demo:Demo() cout“析构 Demo.“endl; ,int main()

13、 cout“在main函数中。“endl; try cout“在try块中,调用MyFunc()。“ endl; MyFunc(); catch( Expt E ) cout“在catch异常处理程序中。“endl; cout“捕获到Expt类型异常:“; coutE.ShowReason()endl; catch( char *str ) cout“捕获到其他的异常:“strendl; cout“回到main函数。从这里恢复执行。“ endl; return 0; ,27,程序运行时输出: 在main函数中。 在try块中,调用MyFunc()。 构造 Demo. 在MyFunc()中抛掷E

14、xpt类异常。 析构 Demo. 在catch异常处理程序中。 捕获到Expt类型异常:Expt类异常。 回到main函数。从这里恢复执行。,28,15.2.2 退化为普通错误处理,如下面的程序是退化为普通错误处理的异常方式:,f1503.cpp,#include #include using namespace std; int main(int argc, char* argv) ifstream in(argv1); try if(!in) throw string(argv1); catch(string s) couts+“ File Opening Error.n“; return

15、 1; for(string s; getline(in, s); coutsendl );,在这种简单应用的场合,没必要使用异常,可以用下列代码替代: if(!in) cout“Error Opening File”argv1“n”; return 1; for(string s; getline(in, s); coutsendl );,15.2.3 跨越函数的异常处理,在f1502.cpp中,是在出现错误时,从出现错误处到处理错误处,沿着函数调用的足迹,一步步往上返回,几经周折后,出错信息早已丢失,剩下的只有出错状态,程序f1504.cpp改写为异常处理方式处理,就可以实现跨函数的大跳转

16、了:,f1504.cpp,#include #include using namespace std; void procFileName(string s); void procOpenMode(string s); void openIn(string s); void openOut(string s); int main() procFileName(“iabc“); procFileName(“oabc“); void procFileName(string s) try for(char c=0; c=9; c+) procOpenMode(s + c+“.txt“); catch

17、(string s) cout“error opening “s“ not existed.n“;,void procOpenMode(string s) if(s0=i) openIn(s); else openOut(s); void openIn(string s) ifstream in(s.c_str(); if(!in) throw s+“ inFile“; for(string line; getline(in, line); coutline“n“); void openOut(string s) fstream out(s.c_str(),ios:in|ios:out|ios

18、:ate); if(!out) throw s+string(“ outFile“); outs+“ outFile is ok.n“; couts+“ is here.n“;,15.3.1 类型匹配,try语句中,涉及到类的,本质上都是函数调用。 throw A() 异常机制与函数机制正交(互不干涉),但捕捉的方式是基于类型匹配。捕捉相当于函数返回类型的匹配,而不是函数参数的匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题,如:,f1506.cpp,#include using namespace std; class A; class B; int main() try int j=

19、0; double d=2.3; char str20=“Hello“; couta; switch(a) case 1: throw d; case 2: throw j; case 3: throw str; case 4: throw A(); case 5: throw B(); default: cout“No throws here.n“; ,catch(int) cout“int exception.n“; catch(double) cout“double exception.n“; catch(char*) cout“char* exception.n“; catch(A)

20、cout“class A exception.n“; catch(B) cout“class B exception.n“; cout“Thats ok.n“; /=,catch代码块必须出现在try后,并且在try块后可以出现多个catch代码块,以捕捉各种不同类型的抛掷。 异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作,因此发生异常现象的地方,一定是某个实体出了差错,该实体所对应的数据类型便作为抛掷和捕捉的依据。,抛掷的实体已经存在,不需要通过类型转换来创建一个临时实体,实参与形参也不能用相容类型提升这一规则。另一方面,捕捉处理并不一定需要实参传递。,异常捕捉的类型匹配之苛

21、刻程度可以和模板的类型匹配媲美,它不允许相容类型的隐式转换,比如,抛掷char类型用int型就捕捉不到例如下列代码不会输出“int exception.”,从而也不会输出“Thats ok.” 因为出现异常后提示退出 int main() try throw H; catch(int) cout“int exception.n“; cout“Thats ok.n“; ,15.3.2 撒网捕捉,程序中可以设置一道道捕捉的关卡,如果抛掷的异常,循着调用链往上,在最近的捕捉关卡未被捉住,则还会被更上端的捕捉关卡追捕,直逼到系统的最后一道防线, terminate.,15.4.1 申述异常,异常抛掷后

22、总是沿着函数调用链往上,直到被某个函数捉住,因此,异常抛掷、捕捉以及处理都依附于函数,函数承载着异常。 上下游函数的联系,可以通过异常申述来获得,异常申述就是在函数声明和函数定义的头部加上可能抛掷的异常集合,如:,异常申述示例,void f() throw(A,B); void g(); void h() throw(); 说明f函数可能会抛掷出A和B类的异常,g函数内可能会抛掷出任何类型的异常,h函数内不会抛掷出任何异常。 如果h函数抛出异常或者f函数抛掷出非A或非B类的异常,这是没有预料到的,称其为未料到异常。,异常申述是一种对设计的描述,从而给程序员一个编程的参照,所以应作为界面,而放在

23、函数声明中,并通过头文件的形式,扩散到程序员那里。 其次,要使用异常申述,函数声明和函数定义中的异常申述必须保持一致,否则无法一一对应。,没有申述的函数,默认为任何抛掷都可穿透该函数,声明: class A; class B; void f1()throw(A,B); void f2(); void f3()throw(); - 对于函数g中的调用,可能捕捉到函数f1的和函数f2的异常抛掷,但捕捉不到函数f3中的任何抛掷VC中可以 注:同一个函数,其声明与定义中的申述应一致,class A; class B; void f1()throw(A,B) if(.) throw A(); void

24、f2() if(.) throw B(); void f3()throw() if(.) throw A(); void g() try f1(); f2(); f3(); catch(A) cout“exception in An”; catch(B) cout“exception in Bn”; ,15.4.2 捉不住处理,如果代码中没有彻底捕捉的异常处理,则对于抛掷的异常,有可能发生捕捉不住的情况。如向量的下标溢出时,系统会抛掷一个runtime_error异常,但许多程序都不屑一捕,因为程序都调通了,逻辑可靠,不会出现下标溢出的错误了。这类异常只能被系统默认的“强制捕捉器” termi

25、nate 捕捉了。 Terminate是系统资源,默认操作是系统的abort函数,从而无条件地终止程序的执行。,另外,未料到异常,如果不去理它,被系统截获后会转而执行unexpected函数,而unexpected函数的默认行为是执行terminate函数。如:,捉不住处理: 抛掷而无布网捕捉的异常将直逼系统的最后一道防线,void f() if(.) throw A; void g() try f(); catch(B) cout“exception Bn”; int main() g(); throw A将穿透函数f,g和main,抵达系统的最后一道防线激发terminate函数该函数调用

26、引起运行终止的abort函数 最后一道防线的函数可以由程序员设置从而规定其终止前的行为,可以通过set_terminate函数修改捕捉不住异常的默认处理器,从而使得发生捉不住异常时,被自定义函数处理: void myTerminate()cout“HereIsMyTerminaten”; set_terminate(myTerminate); set_terminate函数在头文件exception中声明,参数为函数指针void(*)().,15.6.1 构造函数的错误处理,构造函数正象一个封闭的模块,输出状态是一个新创对象任何创建过程中的错误(例如,动态内存申请失败等)都会导致模板的出乎意料

27、这时候的状态是不能接续后继操作的,如:捆绑对象的操作因为没有对象而招致失败如果敢于正常返回,则又招致荒谬的结果如:,class Date int year, month, day; void init(int y, int m, int d) if(y12|d31) return; /? public: Date(int y=2000, int m=1, int d=1) :year(y),month(m),day(d) init(); void print() coutyear“-”month“-” day“n”; ; int main() Date ad(2000,13,1); ad.pr

28、int(); / 荒谬:月份为13 ,异常操作应该恢复到对类对象进行创建和使用以前的状态,void f() Date(2000,13,1); ad.print(); int main() try f(); /其他操作 catch(out_of_range) cout“对象创建失败,改换门庭”; ,class Date int year, month, day; void init(int y, int m, int d) if(y12|d31) throw out_of_range; public: Date(int y=2000, int m=1, int d=1) :year(y),mon

29、th(m),day(d) init(); void print() coutyear“-”month“-” day“n”; ;,构造函数没有返回类型,无法通过返回值来报告运行状态,所以只通过一种非函数机制的途径,即异常机制,来解决构造函数的出错问题。,15.7.1 另一种循环控制法,多重循环控制中,当某个条件满足时,需要立刻退出所有循环体时,一般用goto语句反而比较现实否则一重一重地退,编程复杂,性能也连累这是在一个函数中时的做法 在一个循环中,遇到一个,当某个条件满足时,需要立刻退出函数函数调用链调用链所在的循环时,就不能用goto语句,因为不在同一个函数中此时,程序员渴望函数能奇迹般地跳

30、跃用异常控制结构便能满足编程要求如图:,图15-6 另一种控制结构,throw,catch,try,不能说异常发生一定是一种错误,它已经成为了一种有效的控制手段。 异常并非一定是针对错误时刻处理。,15.7.2 递归控制法,递归函数本身就是一个深不可测的调用链,要立刻从调用链中彻底退出,若走函数控制这条线,那么,必须逐个进行函数返回 而异常控制可以实现瞬间跳跃 如求解”n皇后问题”,代码如下:,f1514.cpp,#include #include #include using namespace std; typedef vector Mat; void queen(int r, int c

31、, Mat); void print(const Mat ,void queen(int r, int c, Mat m) mrc = 2; for(int k=1; k=0) mr+kc-k=1; if(c+km.size() mr+kc+k=1; for(int j=0; jm.size(); +j) if(mr+1j=0) if(r+2=m.size() mr+1j=2; throw m; else queen(r+1,j,m); void print(const Mat,上机,1 测试const (1) void faa(const int 2 在你的函数中使用异常处理机制 try t

32、hrow catch,请分别写出输入08执行结果,class A; class B; void ff() throw(A) throw A(); class CExp public : char name20; int kk; CExp(char *str)strcpy(name,str); ;,int main() /ff(); try int j=0; double d=2.3; char str20=“Hello“; string str1=“String“; couta; switch(a) case 1: throw d; case 2: throw j; case 3: throw str; case 4: throw A(); case 5: throw B(); case 6: str1+=“6“; throw str1; case 7: str1+=“7“; throw (string *) ,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1