一个好的程序不仅要保证能实现所需要的功能,而且还应该有很好的容错能力。在程序运行过程中如果有异常情况出现,程序本身应该能解决这些异常,而不是死机。本章介绍异常处理的基本概念、C++异常处理语句、析构函数与异常处理。通过本章的学习,掌握了C++异常处理的机制,我们就可以在编制程序时灵活地加以运用,从而使我们编制的程序在遇到异常情况时能摆脱大的影响,避免出现死机等现象。
1.1 异常处理语句
C++处理异常的机制由3个部分组成:检查(try)、抛出(throw)和捕捉(catch)。把需要检查的语句放在try块中,throw用来当出现异常时抛出一个异常信息,而catch则用来捕捉异常信息,如果捕捉到了异常信息,就处理它。try-throw-catch构成了C++异常处理的基本结构。
try-throw-catch结构形式如下:
try{
…
if(表达式1) throw x1
…
if(表达式2) throw x2
…
if(表达式n) throw xn
…
}
catch(异常类型声明1)
{ 异常处理语句序列 }
catch(异常类型声明2)
{ 异常处理语句序列 }
…
catch(异常类型声明n)
{ 异常处理语句序列 }
在try-throw-catch结构中,try语句块内为需要受保护的待检测异常的语句序列,如果怀疑某段程序代码在执行时有可能发生异常,就将它放入try语句块中。当这段代码的执行出现异常时,即某个if语句中的表达式的值为真时,会用其中的throw语句来抛掷这个异常。
throw语句语法:
throw表达式;
throw语句是在程序执行发生了异常时用来抛掷这个异常的,其中表达式的值可以是int、float、字符串、类类型等,把异常抛掷给相应的处理者,即类型匹配的catch语句块。如果程序中有多处需要抛掷异常,应该用不同类型的操作数来互相区别。throw抛出的异常,通常是被catch语句捕获。
catch语句块是紧跟在try语句块后面的,即try块和catch块作为一个整体出现,catch块是try-catch结构中的一部分,必须紧跟在try块之后,不能单独使用,在二者之间也不能插入其他语句。但是在一个try-catch结构中,可以只有try块而无catch块。即在本函数中只检查而不处理,把catch块放在其他函数中。一个try-catch结构中只能有一个try块,但却可以有多个catch块,以便与不同类型的异常信息匹配。在执行try块中的语句时如果出现异常执行了throw语句,系统会根据throw抛出的异常信息类型按catch块出现的次序,依次检查每个catch参数表中的异常声明类型与抛掷的异常信息类型是否匹配,当匹配时,该catch块就捕获这个异常,执行catch块中的异常处理语句来处理该异常。
在catch参数表中,一般只写异常信息的类型名,如:catch(double)。
系统只检查所抛掷的异常信息类型是否与catch参数表中的异常声明类型相匹配,而不检查它们的值。假如变量a,b,c都是int型,即使它们的值不同,在throw语句中写throw a、throw b或throw c的作用也均是相同的。因此,如果需要检测多个不同的异常信息,应当由throw抛出不同类型的异常信息。
异常信息类型可以是C++系统预定义的标准类型,也可以是用户自定义的类型(如结构体或类)。如果由throw抛出的异常信息属于该类型或其子类型,则catch与throw二者匹配,catch捕获该异常信息。注意:系统在检查异常信息数据类型的匹配时,不会进行数据类型的默认转换,只有与所抛掷的异常信息类型精确匹配的catch块才会捕获这个异常。
在catch参数表中,除了指定异常信息的类型名外,还可以指定变量名,如:catch(double d)。
此时,如果throw抛出的异常信息是double型的变量a,则catch在捕获异常信息a的同时,还使d获得a的值。如果我们希望在捕获异常信息时,还能利用throw抛出的异常信息的值,这时就需要在catch参数表中写出变量名。如:
catch(doubled){ cout<<"throw"<<d; }
这时会输出d的值(也就是a值)。
当抛出的是类对象时,有时希望在catch块中显示该对象中的某些信息。
#include <iostream>
using namespace std;
#include <cmath>
int main( )
{
double a, b, c;
double disc;
cout << "Please Enter a,b,c:";
cin >> a >> b >> c;
try {
if ( a == 0 ) throw 0;
else
{ disc = b*b-4*a*c; //计算平方根下的值
if ( disc < 0 ) throw "b*b-4*a*c < 0";
cout << "x1 = " << ( -b + sqrt(disc) ) / (2*a) << endl;
cout << "x2 = " << ( -b - sqrt(disc) ) / (2*a) << endl;
}
}
catch( int ) //用catch捕捉a=0的异常信息并作相应处理
{ cout << "a = " << a << endl << "This is not fit for a." << endl; }
catch( const char *s ) //用catch捕捉b*b-4*a*c<0异常信息并作相应处理
{ cout << s << endl << "This is not fit for a, b, c." << endl; }
//system("pause");
return 0;
}
#include <iostream>
using namespace std;
int main( )
{
void Func1( );
try
{ Func1( ); } //调用Func1函数
catch(double)
{ cout << "OK0!" << endl; }
cout << "end0" << endl;
//system("pause");
return 0;
}
void Func1( )
{
void Func2( );
try
{ Func2( ); } //调用Func2函数
catch(char)
{ cout <<"OK1!" << endl; }
cout << "end1" << endl;
}
void Func2( )
{
double a = 0;
try
{ throw a; } //抛出double类型异常信息
catch(float)
{ cout << "OK2!" << endl; }
cout << "end2" << endl;
}
try块的嵌套异常处理语句
1.2 在函数中处理异常
#include<iostream>
using namespace std;
void check(int score)
{
try{
if(score>100) throw "成绩超高!";
else if(score<60) throw "成绩不及格!";
else cout<<"the score is OK..."<<score<<endl;
}
catch(char *s){cout<<s<<endl;}
}
int main()
{ check(45);
check(90);
check(101);
system("pause") ; return 0;
}
1.3 在函数调用中完成异常处理
在处理异常检测时,也可以将抛掷异常的程序代码放在一个函数中,将检测处理异常的函数代码放在另一个函数中,能让异常处理更具灵活性和实用性#include<iostream>
using namespace std;
void check(int score) {
if(score>100) throw "成绩超高!";
else if(score<60) throw "成绩不及格!";
else cout<<"the score is OK..."<<score<<endl;
}
int main()
{ try{ check(45); }
catch(char *s){ cout<<s<<endl; }
try{ check (90); }
catch(char *s){ cout<<s<<endl; }
try{ check (101); }
catch(char *s){ cout<<s<<endl; }
system("pause") ; return 0; }
1.4 限制函数异常
double Deta(double, double, double)throw(double);
double Deta(double, double, double)throw(int, float, double, char);
int Func(int, char); //函数Func可以抛出任何异常
int Func(int, char) throw( );
//不允许函数Func抛出任何异常
2、异常与类
2.1 构造函数、析构函数与异常处理
#include<iostream>
using namespace std;
class Number
{public:
Number(int i)
{
cout<<"in Number constructor..."<<endl;
if(i>100) throw i;
else number=i;
}
~ Number( )
{ cout<<"in Number destructor..."<<endl; }
private:
int number;
};
int main()
{
try{ Number obj(111); }
catch(int e)
{ cout<<"catch an exception when allocated Number "<<e<<endl; }
cout<<"in the end"<<endl;
//system("pause") ;
return 0;
}
2.2 异常类
#include <iostream>
using namespace std;
const int MAX=3;
class Full{}; //堆栈满时抛出的异常类
class Empty{}; //堆栈空时抛出的异常类
class Stack
{public:
Stack(){top=-1;}
void push(int a);
int pop();
private:
int s[MAX];
int top;
};
void Stack::push(int a)
{
if(top>=MAX-1) throw Full();
s[++top]=a;
}
int Stack::pop()
{
if(top<0) throw Empty();
return s[top--];
}
int main(){
Stack s;
try{
s.push(1);
s.push(2);
s.push(3);
s.push(4); //将产生栈满异常
cout<<"s[0]="<<s.pop()<<endl;
cout<<"s[1]="<<s.pop()<<endl;
cout<<"s[2]="<<s.pop()<<endl;
//cout<<"s[3]="<<s.pop()<<endl;//将产生栈空异常
}
catch(Full)
{ cout<<"Exception: Stack Full!"<<endl; }
catch(Empty)
{ cout<<"Exception: Stack Empty!"<<endl; }
//system("pause") ;
return 0;
}
声明了一个同普通类一样的有数据成员和成员函数的较复杂的异常类。
#include <iostream>
using namespace std;
const int MAX=3;
class Full
{public:
Full(int i):a(i){}
int getValue(){return a;}
private:
int a;
};
class Empty{};
class Stack
{public:
Stack(){top=-1;}
void push(int a)
{
if(top>=MAX-1) throw Full(a);
s[++top]=a;
}
int pop()
{
if(top<0) throw Empty();
return s[top--];
}
private:
int s[MAX];
int top;
};
int main()
{
Stack s;
try{
s.push(1);
s.push(2);
s.push(3);
s.push(4);
}
catch(Full e)
{
cout<<"Exception:Stack Full..."<<endl;
cout <<e.getValue()<<" is not be pushed in stack. "<<endl;
}
//system("pause") ;
return 0;
}
2.3 异常对象