异常处理

什么是异常?

异常是程序可能检测到的、运行时刻不正常的情况,例如除数为0、数组越界访问、自由存储空间耗尽、不能连接数据库等。这样的异常存在于程序的正常执行流程之外,而且要求程序立即处理。异常是把代码执行期间发生的错误或例外事件传递给调用方代码的一种特殊手段。
异常的基本结构是:
throw-try-catch:被调函数使用throw抛出一个异常对象,try-catch语句捕获并处理异常。

抛出异常

简单来说就是当调用函数时(包括main函数),如果出现了异常,就是用throw抛出一个异常,说:“我这里出错了,出错的信息是xxx,,,”,那具体是怎么实现的呢?
throw expression;
这个expression的作用就是用来记录异常的相关信息的,这个表达式是比较特殊的,它代表了异常的类型,我们可以使用标准库头文件 stdexcept 中定义的异常类,或者自己定义异常类。注意,这里的expression是一个类类型,我们在throw后写的expression只是异常的类型,所以只用写类型,不需要声明。当执行一个throw时,跟在其后的语句都不会再执行,有点类似return语句,throw抛出异常后,终止当前的函数,并把控制权转移给能处理该异常的代码。

try语句块

try语句块的形式如下:

try {
    program-statements
}catch (exception-declaration) {
    handler-statements
}catch (exception-declaration) {
    handler-statements
}
//可以有多个catch语句块

在将try语句块之前我们先想一个问题,怎么用throw呢?
当我们单独使用throw时,也就是没有try语句块时:

float num1,num2;

cin >> num1 >> num2;
if( num2 == 0 ){
        throw runtime_error("num2 should not be 0!\n");
}       
//terminate();  
cout << "num1 / num2 = " << num1 / num2 <<endl;

如示,这时单独使用,如果num2 == 0,那么就会抛出异常,此时就会跳出当前函数,后面的语句也不会执行了。结果可能会是这样的:
这里写图片描述
由于没有异常处理方法,程序会调用C++的标准库函数terminate(),指示程序非正常退出。
当结合try语句块使用时:

float num1,num2;
try{
    cin >> num1 >> num2;
    if( num2 == 0 ){
    throw runtime_error("num2 should not be 0!\n");
    }       
} catch(runtime_error err){
    cout << err.what() <<endl;
}   
cout << "num1 / num2 = " << num1 / num2 <<endl;

这个时候当你输入“1 0”,throw依然会抛出异常,但是由于此时有try语句块,而且有异常处理办法,则会根据抛出的异常的类型,执行相应的catch语句块。
这里写图片描述
如示,这个时候程序不会退出,而是继续执行catch语句块后的代码。
怎么说呢,异常处理时,通常是在try语句块中直接或者语句块中调用的函数产生一个异常,直接存在或者在被调用函数中的throw语句会抛出一个异常类型,告诉try遇到了啥问题,然后try语句块里的其余代码就不执行了,进入catch语句块。
catch子句包括三个部分,关键字catch、括号内的一个对象声明(称为异常声明,也就是exception-declaration),以及一个异常处理方法。类似于switch语句,try可以和多个catch搭配在一起。那当面对多个catch语句块时,选择哪个来处理异常呢?
这就由throw来决定啦,因为throw抛出的是一个异常的类型,而catch中的异常声明就是用不同的异常类型来声明一个异常对象,选择哪个catch,就看throw抛出哪种类型。

标准异常

C++标准库定义了一组异常类,这些都是异常的类型,可以用来声明异常对象。这些异常类分别定义在4个头文件中:
exception头文件:定义了最通用的异常类exception,它只报告异常的发生不提供任何额外的信息。
new头文件:定义了bad_alloc异常类型。
type_info头文件:定义了bad_cast异常类型。
stdexcept头文件:定义了几种常用的异常类型,
它们是:
exception:最常见的问题
runtime_error:运行时检测出的问题
range_error:运行时错误,结果超出了有意义的值域范围
overflow_error:运行时错误,计算上溢
underflow_error :运行时错误,计算下溢
logic_error:程序逻辑错误
domain_error:逻辑错误,参数对应的结果值不存在
invalid_argument:逻辑错误,无效参数
length_error:逻辑错误,试图创建一个超出该类型最大长度的对象
out_of_range:逻辑错误,使用一个超出有效范围地值
bad_alloc:new运算符失败时抛出的异常类型
bad_cast:dynamic_cast无效时抛出的异常类型

其中,exception、bad_alloc、bad_cast 类的对象只能默认初始化,不能提供初始值,其他类型不允许默认初始化,应该用string或者C风格的字符串初始化。异常类型只定义了一个成员函数what(),返回一个C风格字符串。

捕获异常

catch就是用来捕获异常的,根据catch中的异常声明可以捕获相应的异常类型。这个类型可以是左值,但不可以是右值。
当进入catch子句时,通过throw抛出的异常类型来初始化异常声明中的参数。例如上一个例子中的”num2 should not be 0!\n”初始化了异常对象err。这就类似于函数参数按值传递,该参数是异常对象的一个副本,在catch语句块内改变参数实际上就是改变副本,而不是改变异常对象本身。
当然catch中的异常声明也可以是引用类型。此时修改参数就是修改异常对象,这是catch就可以直接引用throw抛出的异常对象,而不是创建副本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值