一.异常的介绍
当我们编写比较复杂的,个人难以管理的系统时,异常和命名空间最为有用。
异常在工作中经常使用,异常就是在程序运行中发生的难以预料的、不正常的事件而导致偏离正常流程的现象。发生异常将导致正常流程不能进行,就需要对异常进行处理。
异常使得我们能将问题的检测与解决过程分离开来。程序的一部分负责检测问题的出现,然后解决该问题的任务传递给程序的另一部分。检测环节无须知道问题处理模块的细节。
如下,对于除0的处理
cpp
#include <iostream>
using namespace std;
double fun(double a, double b)//除法函数
{
if (b == 0)//除数为0,抛出异常
{
throw b;
}
return a / b; //否则返回两个数的商
}
int main()
{
double res;
try //可能出现异常
{
res = fun(4, 5);
cout << 4 << "/" << 5 << " = " << res << endl;
res = fun(6, 0);//出现异常,函数内部会抛出异常
cout << 6 << "/" << 0 << " = " << res << endl;//这一句没有执行
}
catch (double a) //捕获并处理异常
{
cout << "a=" << a << endl;
cerr << "出现除0错误,程序中止了,请检查!\n";
exit(1); //异常退出程序
}
return 0;
}
异常的抛出和处理主要使用了:throw语句和try-catch语句。
throw语句
如果检测到异常,则抛出异常。
格式为:
cpp
throw 表达式;
表达式的类型作为异常事件的类型,表达式的类型和值将传给捕获该异常的程序。表达式可以是一个基本类型,也可以是一个对象。这个表达式的类型可以和函数的返回值类型不同。
当执行一个throw时,跟在thorw后面的语句将不再被执行。程序的控制权从throw转移到与之匹配的catch模块。
try-catch语句
捕获处理异常的语句是try-catch语句。
该语句格式如下:
cpp
try{ 可能引发异常的语句序列 } //受保护代码
catch(异常类型1 异常变量1){ 处理代码1} //异常处理1
catch(异常类型2 异常变量2){ 处理代码2} //异常处理2
...
catch(异常类型n 异常变量n){ 处理代码n} //异常处理n
catch(...) { 处理代码n+1} //异常处理n+1
其中,关键字try包括的语句称为try子句 。这个语句块中的代码被称为受保护代码 ,可以包含多条语句。受保护代码描述正常的执行流程,但这些语句的执行可能引发异常。如果没有发生异常,try-catch 语句就正常结束,所有的catch都不执行。如果引发了某种类型的异常,就按 catch 子句顺序逐个匹配异常类型,捕获并处理该异常。
**异常是按其类型进行捕获的。**一个catch子句捕获一类异常。异常类型及变量指明要捕获的异常的类型,及接收异常的值。例如,catch(double a),要捕获的异常类型为double,如果真的捕获到该类异常,那么变量a就有这个异常的值,这个值就是前面用throw语句抛出的。
有一种特殊的catch子句,就是catch(...),它的含义是捕获所有异常。在多个 catch 子句中,它应放在最后。
异常处理步骤
在执行try子句中的受保护代码时,如果引发一个异常,系统就到catch子句中寻找处理该异常类型的入口。这个寻找过程称为异常类型匹配,它按如下步骤进行。
(1)由throw语句引发异常事件之后,系统依次检查catch子句以寻找相匹配的处理异常事件入口。如果某个 catch 子句的异常类型与异常事件类型相一致,该异常就被捕获,然后执行该子句的异常处理代码。如果有多个 catch 子句的异常类型相匹配,只执行最前面第一个匹配的异常处理代码。
(2)若没有找到任何相匹配的 catch 子句,该异常就被传递到外层作用域。如果外层作用域是函数,就传递到函数的调用方。
一个异常的生命期从被throw抛出来,然后被某个catch子句捕获,其生命期就结束了。一个异常从抛出来到被捕获,可能穿越多层作用域或函数调用。如果到main函数都未被捕获,将导致程序终止。
例如下面的程序
cpp
#include <iostream>
#include <string>
using namespace std;
class DivdeByZeroException //定义除0异常类
{
string message; //记录异常信息
public:
DivdeByZeroException() :message("除0错误!\n") {}
const string& what() { return message; }
};
double fun(int a, int b) //除法函数
{
if (b == 0) //抛出异常
{
throw DivdeByZeroException{}; //异常类型为DivdeByZeroException
}
return(double)a / b; //返回两个数的商
}
int main()
{
int n1, n2;
double res;
cout << "请输入两个整数 : ";
while (cin >> n1 >> n2)
{
try //受保护代码
{
res = fun(n1, n2);
cout << n1 << "/" << n2 << " = " << res << endl;
}
catch (DivdeByZeroException ex) //捕获并处理异常
{
cout << ex.what();
}
cout << "请输入两个整数 : ";
}
return 0;
}
未捕获异常的处理
如果一个异常没有被catch捕获,这种情况就叫未捕获异常。如果一个异常没有被捕获,则系统将自动调用abort()函数来终止程序执行。
cpp
class A {};
class B {};
void f()
{
int err = 1;
if (err)
{
cout << " 抛出异常 B" << endl;
throw B(); //抛出异常,类型为B
}
}
int main()
{
try //保护代码
{
f();//函数内部抛出异常
}
catch (A) //捕获A类型异常
{
cout << " 捕获异常类型 A" << endl;;
exit(1);
}
cout << " 再见" << endl;;
return 0;
}
上述代码,抛出的是B类异常,但捕获的是A类异常,导致异常没有被捕获。程序运行结果如下: