c++中的异常处理
c++是一门复杂的语言,程序的运行环境也是千奇百怪。因此总会有异常出现,本文主要就是讨论一下c++程序中的异常处理问题。
异常对象
讲到异常,这里就要说明一下,什么是异常?在c++中,异常就是指异常对象。而异常对象是异常类的实例化,异常类是我们定义的用来表示程序错误信息的数据抽象。下图便是标准库异常类的继承体系。 [exception](/assets/img/exception/exception.jpg)
由上图我们可以看到,异常类的继承体系中基类是exception
类。整个关系可以看成是按照层次关系组织的,层次越低,表示的异常情况就越特殊。
一般情况下,每个异常类都定义的有: 1. 拷贝构造函数; 2.
拷贝赋值运算符; 3. 虚析构函数; 4.
一个名叫what
的虚成员函数。没有参数,返回一个const char*
的指针指向一个以null
结尾的字符数组,并且确保不会抛出任何异常。
函数try语句块
一般异常处理部分使用try语句块处理异常,try语句块以关键字try开始,并以一个或多个catch子句结束。try语句块中代码抛出的异常通常会被某个catch子句处理。因为catch子句“处理”异常,所以它们也被称作异常处理代码。其一般形式如下:
1
2
3
4
5
6
try{
//program-statements
}
catch(exception-declaration){
//handler-statements
}
1 |
|
抛出异常(throw)
抛出异常一般指的是抛出一个异常对象。一般使用如下语句进行异常的抛出。
throw exception1; //其中,exception1是一个异常对象。
这样的形式特别像return
语句。它们的作用机理也会有些许相似,都提供一个待接收的对象。
捕获异常(catch)
捕获异常也是指捕获一个异常对象。捕获的过程也就是参数匹配的过程。一般捕获异常使用如下形式进行:
1
2
3
4//exception-declaration通常与函数形参的声明形式一样,可以与throw抛出的异常对象进行参数匹配。
catch(exception-declaration){
//handler-statements
}
但是一般情况下,并不会如此顺利,往往一个更完整的异常匹配过程如下: - 对于在一个try语句块内有异常抛出,则检查与该try块关联的catch子句,如果找到了匹配的catch,就使用该catch处理异常。否则,如果该try语句嵌套在其他try块中,则继续检查与外层try匹配的catch子句。如果仍然没有找到匹配的catch,则退出当前这个主调函数,如果调用语句也是在一个try语句块内,继续在调用了刚刚退出的这个函数的try代码块中寻找。直到找到与之匹配的catch,并使用该catch处理异常。然后执行完这个catch子句后,找到与这个catch对应的try块关联的最后一个catch子句之后的点,并从这里继续执行。上述过程也叫作栈展开。
栈展开过程沿着嵌套函数的调用链不断查找,直到找到了与异常匹配的catch子句为止;或者也可能一直没找到匹配的catch语句,则退出主函数后查找过程终止。(如果找不到匹配的catch时,程序会调用标准库函数
terminate
终止程序)
异常捕获中的参数匹配规则
上文说过,catch语句的捕获与throw语句的抛出异常之间会进行参数匹配。匹配规则类似函数的形参与实参的匹配,但是也不是完全相同。
一般而言要求异常的类型与catch声明的类型是精确匹配的,匹配时一般只有如下三种类型转换可用: 1. 允许从非常量向常量的类型转换,也就是说,一条非常量1对象的throw语句可以匹配一个接受常量引用的catch语句; 2. 允许从派生类向基类的类型转换; 3. 数组被转换成指向数组(元素)类型的指针,函数被转换成指向该函数类型的指针。
除此之外,包括标准算数类型的转换和类类型转换在内,其他所有转换规则都不能在匹配catch的过程中使用。
参考资料
- [1] C++ Primer(第5版)