异常处理也是我经常学了就忘的东西,因为学生时代的课程设计往往情况单一,往往用不到异常处理,再加上我本人经常懒得去考虑异常检测处理的情况,这一部分的知识一直学了就忘,今天利用这个机会好好记录,重新学习一下。
异常顾名思义,是程序在运行过程中遇到的无法处理的问题,超出了程序解决的能力。典型的异常有两点:一是失去数据库连接,其二就是遭遇意外输入。当程序遇到异常的过程中,检测到异常的部分应该能发出某种信号表明其遇到了异常情况,它不需要确定异常发生的代码处,只需表明当下确实有异常的出现即可。如果程序中含有可能发生异常的代码,那么也应当有专门处理异常的方法,例如遇到异常输入时让用户重新进行输入,遭遇数据库连接丢失时发出警告。
C++异常处理机制由异常检测和异常处理两部分组成。其中异常处理包括:
throw表达式,异常检测部分使用throw表达式来抛出它所遇到的无法解决的问题。
try语句块,异常处理部分使用try语句块处理异常,在try中代码抛出的异常通常会被某个对应的catch子语句捕捉处理。、
同时还有一套异常类用于传递异常的具体信息。
1. throw表达式
throw表达式运用非常简单,在特定位置处抛出一个异常。范例代码如下:
//将两本相同isbn的书籍进行相加
//当输入的两本书isbn不相同时抛出异常
if(book1.isbn() != book2.isbn()){
throw runtime_error("Data must refer to same isbn");
}
若ISBN值不一样时就会抛出异常,该异常的类型是runtime_error,抛出这个异常会终止当前函数运行,并将控制权转移给可以处理该异常的代码块。
runtime_error异常是标准库异常的一种,定义在stdexcept头文件中。
2. trycatch语句块
trycatch的通用语法形式为:
try{
program-statements
}catch(exception-declaration){
handler-statements
}catch(exception-declaration){
handler-statements
}...
其中catch子句包括三部分,catch关键字,括号内的(可能未命名)对象声明(异常声明),以及一个块。需要注意的是,在try中声明的对象即便是catch中也无法使用。
针对上述的相加功能,我们可以利用trycatch来抛出处理异常。
while(cin >> item1 >> item2){
try{
//执行相加代码
//如果失败则抛出异常
}catch(runtime_error err){
cout << err.what()
<< "\nTry again? y or n?" << endl;
char c;
cin >> c;
if(!cin || c == 'n') break;
}
}
处理异常时我们输出了一个异常信息err.what(),实际上每个标准库异常类都定义了名为what()的成员函数,它没有输入参数,返回值为C风格字符串。
还需要注意的是对抛出的异常如何寻找对应的catch语句进行处理:当异常被抛出时,首先找到抛出异常的函数,寻找是否有匹配的catch,如果没有则立即终止该函数,并在调用该函数的函数中进行寻找,以此类推,直到找到匹配的catch为止。
如果最终仍未找到对应catch语句,程序会转到名为terminate的标准库函数,该函数行为与系统有关,一般来说调用该函数会使得程序非正常停止。
3. 标准异常
C++定义了一组类用于报告标准库函数遇到的问题,分别定义在4个头文件中
- exception头文件定义了最通用的异常类exception,只报告异常的发生而不提供任何额外信息
- stdexcept头文件定义了几种常用的异常类
- new头文件定义了bad_alloc异常类型
- type_info头文件定义了bad_cast异常类型
其中stdexcept定义的异常类是我们平时最有可能遇到的,例如:
exception | 最常见的问题 |
runtime_error | 运行时才能发现的问题 |
range_error | 运行时错误,生成结果超出了有意义的值域 |
overflow_error | 运行时错误,计算上溢 |
underflow_error | 运行时错误,计算下溢 |
logic_error | 程序逻辑错误 |
domain_error | 参数对应值不存在 |
invalid_error | 无效参数 |
length_error | 试图创建一个超出最大长度的对象 |
out_of_range | 使用一个超出有效范围的值 |
另外需要注意的是,当我们使用exception、bad_alloc、bad_cast对象时不允许为他们赋初始值。而其他异常类型则相反,应该使用string对象或c风格字符串进行初始化而不允许默认初始化。