一个程序的功能是有限的,那些超出函数正常功能范围的行为称为异常,最常见的异常包括数据库连接断开、遇到意外输入。异常实际是系统中最难设计的一部分。
一、throw表达式
程序异常检测部分使用throw表达式引发一个异常。throw后的表达式类型就是异常类型,来看一个最简单的类型:
void printAge(int age)
{
if(age<0)
throw std::runtime_error("Age < 0");
std::cout<<"age"<<age<<std::endl;
}
int main()
{
printAge(-22);
}
运行结果如下:
terminate called after throwing an instance of 'std::runtime_error' what(): Age < 0 Aborted (core dumped)
这就是一个典型的意外输入的例子。异常的类型是throw后表达式类型,std::runtime_error是一个标准异常。这个函数在遇到一个负数年龄输入时将会抛出异常,抛出异常意味着:
- 终止当前函数
- 将控制权转移给能处理异常的代码
那么标准异常是什么意思呢?标准异常是标准库用于报告标准库函数遇到的问题,用户代码也可以使用这些异常来处理异常,标准异常分别定义在四个头文件中:
- exception头文件定义了最通用的异常类
exception
,它只报告异常的发生,不提供额外信息; - stdexcept头文件定义了几种常见的异常类,见表1;
- new头文件定义了bad_alloc异常类型;
- type_info 定义了bad_cast异常类型;
异常名称 | 含义 |
---|---|
exception | 最常见的问题 |
runtime_error | 只有运行时才能检测出的问题 |
range_error | 运行时错误,生成的结果超出了有意义的至于范围 |
overflow_error | 运行时错误,上溢 |
underflow_error | 运行时错误,下溢 |
logic_error | 程序逻辑错误 |
domain_error | 逻辑错误:参数对应结果值不存在 |
invalid_argument | 逻辑错误:无效参数 |
length_error | 逻辑错误:试图创建一个超出该类型最大长度的对象 |
out_of_range | 逻辑错误:试图使用一个超出有效范围的值 |
标准异常只定义了几种运算,包括创建或拷贝异常类型对象,以及异常类型对象赋值;只能使用默认初始化的方式初始化exception、bad_alloc和bad_cast,不允许为这些对象提供初值;其他异常类型的行为则必须使用string对象或C-style字符串类型初始化对象。
异常类型只定义了一个名为what的成员函数,该函数没有任何参数,返回值是一个指向C-style类型指向常量字符串指针,其目的是为了提供异常的文本信息,what返回值内容与异常对象有关,如果定义了初始值那么返回这个字符串,如果没有,那么由编译器决定。
二、try语句块
语法结构如下:
try{
program-statements;
}catch(exception-declaration){
handler-statements;
}catch(exception-declaration){
handler-statements;
}//...
一个try后面跟着语句块,N个catch子句块。catch子句块包含三个部分:关键字catch、括号内一个对象(可能未命名)声明(称为异常声明,exception declaration)以及一个块,try语句组成程序正常逻辑,try内部的语句声明在块外是无法访问的,就算是catch也不行。还是前面那个例子:
void printAge(int age)
{
try{
if(age<0)
throw std::runtime_error("Age < 0");
std::cout<<"age"<<age<<std::endl;
}catch(std::runtime_error e) {
std::cout<<e.what();
}
}