160 likes | 298 Views
第十章 异常处理. 类的设计者很难考虑各种错误 类的使用者无法检查何时出现错误. 对象类型决定了所抛出的是哪种异常. 10.1 用异常来处理错误 错误检查(类:抛出异常) C++ 异常处理机制 错误处理(使用者:具体处理) 抛出异常: throw 异常对象; 处理异常: try { // 检测抛出异常对象范围 } catch ( 异常对象类型) {
E N D
第十章 异常处理 类的设计者很难考虑各种错误 类的使用者无法检查何时出现错误
对象类型决定了所抛出的是哪种异常 10.1 用异常来处理错误 错误检查(类:抛出异常) C++异常处理机制 错误处理(使用者:具体处理) 抛出异常: throw异常对象; 处理异常: try { //检测抛出异常对象范围 } catch(异常对象类型) { //异常处理 } 越界错误: class Vector{ int *p; int sz; public: Vector(int s) { p=new int [s]; sz=s; } ~Vector( ) { delete [ ] p ; } int size( ) { return sz; } int& operator[ ] (int i); class Range { }; }; throw只有在try块内抛出才有效 异常类
异常类对象 Range re; throw re; int& Vector:: operator[ ] (int i); { if(i>=0 && i<sz) return p[i]; throw Range( ); } 如何使用: #include<iostream.h> #include<stdlib.h> void f( Vector& v ) { //…… try { v[0]=1; v[v.size( )+10]=10; v[0]=0; } catch( Vector:: Range ) { cout<<“Vector: out of Range”<<endl; exit(1); } cout<<“pass”<<endl; } 发现越界,抛出异常对象 跳出try块,进行catch参数匹配 无异常 跳过catch块,往下执行
throw抛出异常,但当前函数无try块或catch未捕获到throw抛出异常,但当前函数无try块或catch未捕获到 向上层调用函数继续抛出 直到最外层仍不处理所抛出的异常 自动调用void terminate( )中断程序执行 class X { }; class X { }; void f1( ) void f1( ) { { throw X( ); throw X( ); } } void f2( ) void f2( ) { { f1( ); f1( ); } } void f3( ) { void main( ) f2( ); { } f2( ); void f4( ) } { try { f3( ); } catch(X) { //…… } } terminate( )调用abort( )
指向terminate( )原来调用的函数 指向terminate( )现在要调用的函数 可用set_terminate( )改变terminate( )的缺省行为。 set_terminate( )的原型为: typedef void ( *PFV) ( ); PFV set_terminate(PFV); #include<iostream.h> #include<exception.h> typedef void ( *PFV) ( ); void error_message( ) { cout<<“error”<<endl; } void f( ) { PFV p=set_terminate(&error_message); //…… set_terminate(p); //…… }
抛出的异常对象,可利用其数据成员和成员函数记录一些异常状态。抛出的异常对象,可利用其数据成员和成员函数记录一些异常状态。 class Vector{ int *p; int sz; public: class Range { public: int index; Range(int i): index(i) { } }; Vector(int s) { p=new int [s]; sz=s; } ~Vector( ) { delete [ ] p ; } int size( ) { return sz; } int& operator[ ] (int i); }; int& Vector:: operator[ ] (int i); { if(i>=0 && i<sz) return p[i]; throw Range( i ); } void f( Vector& v ) { try { v[0]=1; v[v.size( )+10]=10; } catch( Vector:: Range r ) { cout<<“bad index”<<r.index<<endl; } } catch用捕获的异常对象初始化r
10.2 多个异常 10.2.1 处理多个异常 #include<iostream.h> #include <stdlib.h> class Vector{ int *p; int sz; enum {max=3200}; public: class Range { }; class Size { }; Vector(int s); int size( ) const { return sz; } int& operator[ ] (int i); }; Vector:: Vector(int size) { if(size<0 || max<size) throw Size( ); p=new int[size]; sz=size; } 下标越界异常 数组尺寸异常 尺寸异常发生在构造函数中
try块有异常抛出 检测第一个catch 匹配 Vector的使用者处理异常: void f( ) { try { use_Vectors( ); } catch(Vector:: Range) { cout<<“Vector:: Range error”; exit(1); } catch(Vector:: Size) { cout<<“Vector:: Size error”; exit(1); } } 若use_Vectors定义为: void use_Vectors( ) { Vector ve( 32010 ); //…… } 输出结果? 若use_Vectors定义为: void use_Vectors( ) { Vector ve( 100 ); ve[105]=25; //…… } 输出结果? No yes 第一个catch块 检测第二个catch yes 匹配 No 第二个catch块 下一catch 跳过其它catch
并非所有异常处理都放在一个函数里 #include<iostream.h> #include<stdlib.h> #include”Vectro.h” void use_Vector( Vector &v, int range ) { v[range]=21; } Vector create_Vector(int size) { return Vector(size); } void f2( Vector &v, int range) { try { use_Vector(v, range); } catch(Vector:: Range) { cout<<“Vector:: Range error”<<endl; exit(1); } }
void f1( int size, int range) { try { Vector ve=createVectors(size); f2(ve, range); } catch(Vector:: Size) { size=size>0? --size:0; cout<<size<<endl; f1(size, range); } } void main( ) { int size, range; cout<<“input size, range:”; cin>>size>>range; f1(size, range); }
语义相关放在一起 缺点:不能象类对象那样带有异常信息 10.2.2 用枚举组织异常 异常种类繁多 用户难以管理合理地组织 enum Matherr { Overflow, Underflow, Zerodivide /*…*/}; void f( ) { try { //…… } catch(Matherr m) { swith(m) { case Overflow: //…… case Underflow: //…… } } } 10.2.3 用派生类组织异常 class Matherr { /*…*/ }; class Overflow: public Matherr { /*…*/ }; class Underflow: public Matherr { /*…*/ }; //…… try { //…… } catch(Overflow) { /*…*/ } catch(Underflow) { /*…*/ } //…… 缺点:每种异常都要有相应处理程序 基类异常处理
catch(…)可捕获任何异常 缺点:走向另一极端 try { //…… } catch(Overflow) { //处理上溢出 } catch(Matherr) { //处理除上溢出之外的算术库其它异常 } //…… catch的先后次序为:先派生,后基类,最后省略参数 10.2.4 用虚函数组织异常 在Matherr中添加一个Int_overflow异常: class Matherr { //…… void debug_print( ); }; class Int_overflow: public Matherr { public: char * cp; int opr1, opr2; Int_overflow(const char* p, int a, int b) { op=p; opr1=a; opr2=b; } void debug_print( ) { cout<<op<<‘(‘<<opr1<<‘,’<<opr2<<‘)’; } };
void f( ) { try { //抛出Int_overflow异常 } catch(Matherr m) { m.debug_print( ); //…… } } 引入虚函数: class Matherr { //…… virtual void debug_print( ); }; class Int_overflow: public Matherr { public: char * cp; int opr1, opr2; Int_overflow(const char* p, int a, int b) { op=p; opr1=a; opr2=b; } virtual void debug_print( ) { cout<<op<<‘(‘<<opr1<<‘,’<<opr2<<‘)’; } }; f( )中的catch(Matherr m)改为catch(Matherr &m)
f1只处理X,Y及其派生类异常 f2可处理各种异常 10.3 异常的接口说明 10.3.1 异常接口说明形式 一个函数往往处理异常的子集,为了表示函数和异常之间的关系 函数返回值类型 函数名(函数参数)throw(类型1,类型2,…) void f1( ) throw(X,Y); int f2( ); int f3( ) throw( ); 10.3.2 unexpected( )函数 异常接口有说明,但函数未处理 异常被重新抛出; 遇到异常未在接口中说明 调用unexpected( )函数 void f( ) throw(x2,x3) { //未处理x2,x3异常 } void f( ) { try { //…… } catch(x2) { throw; } f3不处理任何异常
原型为:void unexpected( ) 缺省调用terminate( )函数 catch(x3) { throw; } catch(…) { unexpected( ); } }