异常处理对于大型程序,或需长期运行的程序(交换机,路由器上的)是必不可少的,后者很大比例上的程序需要异常控制。当程序遇到自己处理不了的问题如内存边界、错误输入等,我们需要改变程序的执行流,中止正在执行的函数,转向能够对这些错误进行处理的地方。
简单地看,异常处理包括两部分:
1.throw 语句,发现错误的地方通过throw语句抛出相应的异常。
2.try 块,处理异常的部分,以关键字try 开头,后面跟着一个或多个 catch语句(通常也叫异常处理函数 handler).
3.能被抛出的异常类,由系统库定义提供,在throw语句和catch语句之间传递错误的信息。
一个简单的异常程序
#include <iostream>
#include <stdexcept>
using namespace std;
int main(void) {
try{
cout<<"start in try\n";
throw runtime_error("this is an error\n");
cout<<"end in try\n";
}
catch(runtime_error e){
cout<<e.what();
}
cout<<"end in main\n";
return EXIT_SUCCESS;
} |
在try块中我们用throw出一个runtime_error异常,这是c++标准异常之一,使用它需引入头文件
stdexcept。接下来的catch语句我们对其进行捕获,调用其的what函数输出其异常信息,即在throw语句出创建该异常时的字符串参数。
异常程序需要特别注意的地方是如果程序跑出了异常,则中止当前执行,直接转到相应的catch处进行处理,
处理完后并不返回原抛出异常处,而是接着该catch语句顺序往下执行,有些类似switch语句,所以我们看到程序并
没有输出try块中throw语句后面的“end in try”。
注
意抛出异常在正常程序中是有条件的,程序可能在遇到第一个异常时已经执行了很多try块,try块中还可以调用含有try块的函数。遇到异常时,搜索
catch语句的顺序是抛出异常的当前函数,没有则中止当前函数,返回调用其的函数寻找,如此下去。如果到最后也没有找到合适的catch语句,程序会转
向一个库函数(library
function)terminate,其在头文件exception中定义,具体执行过程和平台有关,通常是终止退出(abort)当前程序。
c++中的异常类:
1.头文件exception定义了最基本的异常类exception,它也是异常类体系结构中的基类。
2.stdexcept头文件定义常见的异常,除上面的runtime_error,还有logic_error,以及它们的一些派生类
3.new头文件中定义了bad_alloc异常,当new无法申请到内存时抛出这个异常。
4.type_info头文件定义了bad_cast异常,当用dynamic_cast进行类型转换失败时抛出此异常。
exception,bad_alloc,bad_cast只有默认的构造函数,其它异常也只有一个带一个string参数的构造函数,用以说明该异常的信息。
异常类也只有一个操作,虚函数what,返回c语言类型char *,指向构造函数所接收的string内容。
我们自己也可以定义异常,可以是任何类的对象(object of any type),前提是
该对象可以作为非引用形式的实参,也就是该对象需要能够被复制。
数组与函数
最好不要成为异常,和函数调用类似,抛出一个数组,它被转换为指向其首元素的指针,同样,抛出的函数被转换为指向其的函数指针。
前面提到,throw语句抛出异常后,执行流要跳转去寻找相应的catch语句,当前域的局部变量所占的内存就会被释放,所以如果抛出的是指针的话,等到catch语句需要它进行相应处理时,其所指向的东西
很可能已经不可知。
故通常在throw语句中创建一个异常对象,并将其抛出,该对象被传递给相应的catch语句(有的话),在处理完该异常后被析构回收。事实上,异常对象通过拷贝throw语句的结果被创建,所以这个结果必须是能被拷贝的类型。
这种形式的抛异常简单明了,因为抛出的是具体对象,根据其
静态类型,就可以知道是何种异常。
如
果通过解除指针引用的形式抛异常,尤其是基类型的指针此时正指向派生类的对象,那么抛出的异常由该指针的静态类型决定,也就是抛出的是基类型异常,派生类
部分被slice down。最好不通过此种形式抛异常,不好理解不说,还要确保在相应catch语句出,该指针指向的对象仍然存在。
阅读(753) | 评论(0) | 转发(0) |