程序代码编写正确以并且编译正确,不代表程序执行就不会发生问题。例如,下面的代码同样会导致执行错误,
int main() {
int a, b; cin>>a>>b;
cout << a/b << endl; //除零问题
return 0;
}
程序非常简单,编译没有问题。但是,在程序执行时如果输入1和0,就会发生除零错误,然后程序执行终止。事实上,程序在执行过程中不可避免地需要面对许多突发状况,例如,除零错误、数组越界、类型转换失败、打开文件不存在、内存耗尽等,这些往往会导致程序执行处于不正常的状态,我们称之为异常(exception)。
异常会伤害程序执行的一致性,甚至导致程序执行不正常终止。此时,程序员在编写程序时应该针对异常做一些事前防御性措施或者事后补救性措施。其中,事前防御性措施,是指在异常发生之前将其消灭在萌芽状态。例如在做除法运算之前先检测除数是否为0或者接近0,若是则不进行除法运算,从而避免发生除零异常。
但是,即使我们程序员再怎么小心谨慎,再怎么事前防御,也不可能穷举所有异常状况,完全避免异常的发生。这时就要求我们在程序代码中做一些异常发生后的补救性措施,尽量是程序执行恢复到一个一致的状态,避免资源泄漏乃至程序崩溃等问题。为此,C++语言引入了异常处理(ExceptionHandling)的语法机制。
C++异常处理机制
当函数代码可能发生某种异常且暂时无法处理这种异常时,就可以抛出这种异常(即抛出异常);然后,函数调用者就可以检测是否真正发生了异常(即捕获异常),并且说明若发生了这种异常就进行什么样的补救处理(即处理异常)。
为此,C++语言的异常处理机制就提供了如下的语法工具和设施,包括,
(1)throw语句:用于函数抛出特定异常,通知异常发生;
(2)try-catch语句:try用于捕获异常,catch用以处理异常。
抛出异常:throw语句
抛出异常,通过throw语句。例如,
class MyExcp {
string errmsg;
public:
MyExcp(stringmsg) : errmsg(msg) {}
string getmsg() { return errmsg; }
};
class DivByZeroExcp : public MyExcp {
public:
DivByZeroExcp() : MyExcp(“除数为零异常”) {}
}
class LogExcp : public MyExcp {
public:
LogExcp() : MyExcp(“零求对数异常”) {}
}
double divide(double a, double b) {
if ( b < 1e-5 ) throw DivByZeroExcp(); //抛出除零异常
return a/b;
}
double log10(double a, double N) {
if ( a<1e-5 || a ==1.0 || n < 1e-5) throwLogExcp(); //抛出对数异常
return log(n);
}
在这里,MyException是我们定制的异常类,有一个数据成员errmsg用于保存异常的说明信息。然后,在定义divide函数时,如果参数b等于0,就会发生除零错误,因此,我们构造一个异常对象MyException(“除数为零异常”),将其抛出。
可以看出,抛出异常的语法非常简单:throw 异常对象。其中,异常对象可以是包括内置数据类型的任意类型,通常会为抛出的异常定制相应的类。
捕获并处理异常:try-catch语句
注意,在抛出异常时我们的函数并没有处理异常,只是将错误信息保存在抛出的异常对象中。只有当未来有人调用divide函数时,发现divide函数可能会引发除零错误的异常,他就可以通过try-catch语句来捕获并处理这种异常。例如,
int main() {
int a, b; cout<<"请输入两个整数:"<<endl;
while(true) {
cin>>a>>b;
try{ //捕获异常
cout<< divide(a, b) << endl;//可能会产生异常
break;
}catch (DivByZeroExcp e) { //处理异常
cout<< e.getmsg() <<endl;
cout<<"请重新输入两个整数:"<<endl;
}
}
return 0;
}
在main函数中,函数调用divide(a,b)可能会引发除零异常。如果输入正常,没有引发异常,则之后的break语句会跳出循环,于是程序执行正常终止。
如果输入除数为0,则divide函数会抛出除零异常。此时,try语句块首先会捕获到该除零异常;然后,异常处理机制会在try语句块之后寻找可以处理除零异常的catch语句块,也就是参数类型与异常类型相匹配的第一个catch语句块。
找到处理异常的catch语句块,程序执行流程就进入该catch语句块执行其中的代码,即处理异常,这里就是输出异常信息并提示重新输入两个整数。
错误处理完成之后,程序再次恢复到正常的执行流程,继续while循环。

