C之异常处理PPT课件_第1页
C之异常处理PPT课件_第2页
C之异常处理PPT课件_第3页
C之异常处理PPT课件_第4页
C之异常处理PPT课件_第5页
已阅读5页,还剩58页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

2、杂性(Error Processing Complexity) 2. 使用异常(Using Exception) 3.捕捉异常(Catching Exception) 4. 异常申述(Exception Description) 5. 异常继承体系(Exception Inheritance System) 6. 异常应用 (Exception Applications) 7. 非错误处理(Non-Error Processing)第3页/共63页15.1.1 错误种类 C+语言是按函数调用机制展开程序执行的,一般对处理错误的编程有下列常规手段: 1.遇到错误,立即终止程序运行。如打不开文件,

3、或者读不到所要求的数据,则只能终止运行:第4页/共63页 #include #include #include using namespace std; void fn() ifstream in(abc.txt); if(!in) couta; coutsqrt(a*1.0)n) if(a0) cout=size)下标溢出接受外来申请if(vi溢出) 说明资源不足应用模块2应用模块1向量模块第10页/共63页15.1.3 调用链的牵制 函数是栈式管理的,从一个被调用的函数现场要把信息传递到相隔若干调用的主调函数,需要逐层退栈,一边退栈,一边通过函数返回值回馈,而且,每个函数都需要相应的接受判

4、断以及接力传递的工作。如有一个简单的系列文件处理程序,假定文件在打开前,要经历文件名确定和打开方式确定的工作,如图15-2:第11页/共63页图15-2 链式调用与出错后跳跃返回主函数文件名处理打开方式处理打开输入文件打开输出文件出错跳回第12页/共63页 #include #include using namespace std; void procFileName(string s); bool procOpenMode(string s); bool openIn(string s); bool openOut(string s); int main() procFileName(iab

5、c); procFileName(oabc); void procFileName(string s) for(char c=0; c=9; c+) string t = s + c + .txt; if(!procOpenMode(t) couterror opening tn; return; 第13页/共63页 bool procOpenMode(string s) if(s0=i & openIn(s) | s0=o & openOut(s) return true; return false; bool openIn(string s) ifstream in(s.c

6、_str(); if(!in) return false; for(string line; getline(in, line); coutlinen); return true; bool openOut(string s) fstream out(s.c_str(), ios:in|ios:out|ios:ate); if(!out) return false; couts+ is here.n; outs+ is ok.n; return true;第14页/共63页15.2 使用异常 异常是专门针对抽象编程中的一系列错误处理的,C+中不能借助函数机制,因为栈结构的本质是先进后出,依次访

7、问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试,如图:第15页/共63页v错误处理示意:放弃一棵子树,循调用链跳到祖先函数发现错误处第16页/共63页 异常超脱于函数机制,决定了其对函数的跨越式回跳。第17页/共63页15.2.1 异常使用三部曲 1.框定异常(try 语句块) 在祖先函数处,框定可能产生错误的语句序列,它是异常的根据,若不框定异常,则没有异常。 2.定义异常处理(catch 语句块) 将出现异常后的处理过程放在catch块中,以便当异常被抛出,因类型匹配而捕捉时,就处理之。 3.抛掷异常(throw 语句) 在可能产生异常的语句中进行错误检

8、测,有错就抛掷异常 第18页/共63页19异常处理的实现机制 抛掷异常的程序段void Fun().throw 表达式;. 捕获并处理异常的程序段try 复合语句catch(异常类型声明) 复合语句catch(类型 (形参) 复合语句 第19页/共63页20例12-1处理除零异常#include#includeint Div(int x,int y);int Div(int x,int y);int main()int main() trytry cout5/2=Div(5,2)endl; cout5/2=Div(5,2)endl; cout8/0=Div(8,0)endl; cout8/0=

9、Div(8,0)endl; cout7/1=Div(7,1)endl; cout7/1=Div(7,1)endl; catch(int) catch(int) coutexcept of deviding zero.n; coutexcept of deviding zero.n; coutthat is ok.n;coutthat is ok.n; int Div(int x,int y)int Div(int x,int y) if(y=0) throw y;if(y=0) throw y;return x/y;return x/y; 程序运行结果如下:5/2=25/2=2except o

10、f deviding zero.except of deviding zero.that is ok.that is ok.第20页/共63页 前两个步骤是一个函数中定义的,而抛掷异常则可以跨函数使用。 当直接在try语句块中使用时,异常处理退化为一般的错误处理模式。 在try语句块中,会有一些语句调用了其他函数,它们之间则构成一个调用链,在调用链中的某一个结点上,如果出现抛掷语句,则便是一般意义上的异常了。第21页/共63页22异常处理的基本思想函数f()捕获并处理异常函数h() 引发异常函数g()调用者异常传播方向调用关系第22页/共63页23异常处理的实现机制(续) 1 若有异常则通过t

11、hrow操作创建一个异常对象并抛掷。 2 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。 3 如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。 4 catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。 5 如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。第23页/共63页24异常接口声明 可以在函数的声明中列出这个函数可能抛掷的所有异

12、常类型。 例如:void fun() throw(A,B,C,D); 若无异常接口声明,则此函数可以抛掷任何类型的异常。 不抛掷任何类型异常的函数声明如下:void fun() throw();第24页/共63页25异常处理中的构造与析构 找到一个匹配的catch异常处理后 初始化参数。 将从对应的try块开始到异常被抛掷处之间构造(且尚未析构)的所有自动对象进行析构。 从最后一个catch处理之后开始恢复执行。第25页/共63页26例12-2 异常处理时的析构#include void MyFunc( void );class Expt public: Expt(); Expt(); con

13、st 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;第26页/共63页int main() cout在main函数中。endl; try cout在try块中,调用MyFunc()。 endl; MyFunc(); catch( Expt E )

14、 cout在catch异常处理程序中。endl; cout捕获到Expt类型异常:; coutE.ShowReason()endl; catch( char *str ) cout捕获到其他的异常:strendl; cout回到main函数。从这里恢复执行。 endl; return 0;27第27页/共63页程序运行时输出:在main函数中。在try块中,调用MyFunc()。构造 Demo.在MyFunc()中抛掷Expt类异常。析构 Demo.在catch异常处理程序中。捕获到Expt类型异常:Expt类异常。回到main函数。从这里恢复执行。28第28页/共63页15.2.2 退化为普

15、通错误处理 如下面的程序是退化为普通错误处理的异常方式:第29页/共63页 #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 1; for(string s; getline(in, s); coutsendl ); 第30页/共63页 在这种简单应用的场合,没必要使用异常,可以用下列代码替代: i

16、f(!in) cout“Error Opening File”argv1“n”; return 1; for(string s; getline(in, s); coutsendl );第31页/共63页15.2.3 跨越函数的异常处理 在中,是在出现错误时,从出现错误处到处理错误处,沿着函数调用的足迹,一步步往上返回,几经周折后,出错信息早已丢失,剩下的只有出错状态,程序改写为异常处理方式处理,就可以实现跨函数的大跳转了:第32页/共63页 #include #include using namespace std; void procFileName(string s); void pro

17、cOpenMode(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(string s) couterror opening s not existed.n;第33页/共63页 void procOpenMode(string s) if(s0=i) ope

18、nIn(s); else openOut(s); void openIn(string s) ifstream in(s.c_str(); if(!in) throw s+ inFile; for(string line; getline(in, line); coutlinen); void openOut(string s) fstream out(s.c_str(),ios:in|ios:out|ios:ate); if(!out) throw s+string( outFile); outs+ outFile is ok.n; couts+ is here.n;第34页/共63页15.

19、3.1 类型匹配 try语句中,涉及到类的,本质上都是函数调用。 throw A() 异常机制与函数机制正交(互不干涉),但捕捉的方式是基于类型匹配。捕捉相当于函数返回类型的匹配,而不是函数参数的匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题,如:第35页/共63页 #include using namespace std; class A; class B; int main() try int j=0; double d=2.3; char str20=Hello; couta; switch(a) case 1: throw d; case 2: throw j; case 3:

20、 throw str; case 4: throw A(); case 5: throw B(); default: coutNo throws here.n; 第36页/共63页 catch(int) coutint exception.n; catch(double) coutdouble exception.n; catch(char*) coutchar* exception.n; catch(A) coutclass A exception.n; catch(B) coutclass B exception.n; coutThats ok.n; /=第37页/共63页 catch代码

21、块必须出现在try后,并且在try块后可以出现多个catch代码块,以捕捉各种不同类型的抛掷。 异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作,因此发生异常现象的地方,一定是某个实体出了差错,该实体所对应的数据类型便作为抛掷和捕捉的依据。第38页/共63页 抛掷的实体已经存在,不需要通过类型转换来创建一个临时实体,实参与形参也不能用相容类型提升这一规则。另一方面,捕捉处理并不一定需要实参传递。第39页/共63页u 异常捕捉的类型匹配之苛刻程度可以和模板的类型匹配媲美,它不允许相容类型的隐式转换,比如,抛掷char类型用int型就捕捉不到例如下列代码不会输出“int except

22、ion.”,从而也不会输出“Thats ok.” 因为出现异常后提示退出int main()int main() try try throwthrow H; catchcatch(intint) coutint exception.n; coutThats ok.n;第40页/共63页15.3.2 撒网捕捉 程序中可以设置一道道捕捉的关卡,如果抛掷的异常,循着调用链往上,在最近的捕捉关卡未被捉住,则还会被更上端的捕捉关卡追捕,直逼到系统的最后一道防线, terminate.第41页/共63页15.4.1 申述异常 异常抛掷后总是沿着函数调用链往上,直到被某个函数捉住,因此,异常抛掷、捕捉以及处

23、理都依附于函数,函数承载着异常。 上下游函数的联系,可以通过异常申述来获得,异常申述就是在函数声明和函数定义的头部加上可能抛掷的异常集合,如:第42页/共63页异常申述示例 void f() throw(A,B); void g(); void h() throw(); 说明f函数可能会抛掷出A和B类的异常,g函数内可能会抛掷出任何类型的异常,h函数内不会抛掷出任何异常。 如果h函数抛出异常或者f函数抛掷出非A或非B类的异常,这是没有预料到的,称其为未料到异常。第43页/共63页 异常申述是一种对设计的描述,从而给程序员一个编程的参照,所以应作为界面,而放在函数声明中,并通过头文件的形式,扩散

24、到程序员那里。 其次,要使用异常申述,函数声明和函数定义中的异常申述必须保持一致,否则无法一一对应。第44页/共63页没有申述的函数,默认为任何抛掷都可穿透该函数声明: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 f2() if(.) thro

25、w 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”; 第45页/共63页15.4.2 捉不住处理 如果代码中没有彻底捕捉的异常处理,则对于抛掷的异常,有可能发生捕捉不住的情况。如向量的下标溢出时,系统会抛掷一个runtime_error异常,但许多程序都不屑一捕,因为程序都调通了,逻辑可靠,不会出现下标溢出的错误了。这类异常只能被系统默认的“强制捕捉器” terminate 捕捉了。

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

27、函数调用引起运行终止的abort函数最后一道防线的函数可以由程序员设置从而规定其终止前的行为第48页/共63页u可以通过set_terminate函数修改捕捉不住异常的默认处理器,从而使得发生捉不住异常时,被自定义函数处理:u void myTerminate()cout“HereIsMyTerminaten”;u set_terminate(myTerminate);u set_terminate函数在头文件exception中声明,参数为函数指针void(*)().第49页/共63页第50页/共63页15.6.1 构造函数的错误处理 构造函数正象一个封闭的模块,输出状态是一个新创对象任何创

28、建过程中的错误(例如,动态内存申请失败等)都会导致模板的出乎意料这时候的状态是不能接续后继操作的,如:捆绑对象的操作因为没有对象而招致失败如果敢于正常返回,则又招致荒谬的结果如:第51页/共63页class Date int year, month, day; void init(int y, int m, int d) if(y1|m12|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

29、“n”; ;int main() Date ad(2000,13,1); ad.print(); / 荒谬:月份为13第52页/共63页异常操作应该恢复到对类对象进行创建和使用以前的状态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(y1|m12|d31) throw out_of_range; public

30、: Date(int y=2000, int m=1, int d=1) :year(y),month(m),day(d) init(); void print() coutyear“-”month“-” day“n”; ;第53页/共63页 构造函数没有返回类型,无法通过返回值来报告运行状态,所以只通过一种非函数机制的途径,即异常机制,来解决构造函数的出错问题。第54页/共63页15.7.1 另一种循环控制法 多重循环控制中,当某个条件满足时,需要立刻退出所有循环体时,一般用goto语句反而比较现实否则一重一重地退,编程复杂,性能也连累这是在一个函数中时的做法 在一个循环中,遇到一个,当某个

31、条件满足时,需要立刻退出函数函数调用链调用链所在的循环时,就不能用goto语句,因为不在同一个函数中此时,程序员渴望函数能奇迹般地跳跃用异常控制结构便能满足编程要求如图:第55页/共63页图15-6 另一种控制结构 throw catch try第56页/共63页 不能说异常发生一定是一种错误,它已经成为了一种有效的控制手段。 异常并非一定是针对错误时刻处理。第57页/共63页15.7.2 递归控制法u递归函数本身就是一个深不可测的调用链,要立刻从调用链中彻底退出,若走函数控制这条线,那么,必须逐个进行函数返回u而异常控制可以实现瞬间跳跃u如求解”n皇后问题”,代码如下:第58页/共63页 #include #include #inclu

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论