异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持。在c++中异常处理包括:
1. throw 表达式,异常检测部分使用 throw 表达式来表示它遇到了无法处理的问题。我们说 throw 引发 (raise) 了异常。
2. try 语句块 (try block),异常处理部分使用 try 语句块处理异常。try 语句块以关键字 try 开始,并以一个或多个 catch 子句(catch clause) 结束。try 语句块中代码抛出的异常通常会被某个 catch 子句处理。因为 catch 子句 “处理” 异常。所以它们也称作异常处理代码。
3. 一套异常类(exception class),用于在 throw 表达式和相关的 catch 子句之间传递异常的具体信息。
throw 表达式:
程序的异常检测部分使用 throw 表达式引发一个异常。throw 表达式包含关键字 throw 和紧随其后的一个表达式,其中表达式的类型就是抛出的异常类型。throw 表达式后面通常紧跟一个分号,从而构成一条表达式语句。
1 double a, b; 2 cin >> a >> b; 3 if(b == 0){ 4 throw runtime_error("b can't be 0!");//没有添加异常处理代码块(catch),所以会直接终止 5 } 6 cout << a / b << endl;
在这段代码中,如果 b 为 0 就抛出一个异常,该异常时是类型 runtime_error 的对象。抛出异常将终止当前的函数,并把控制权转移给能处理该异常的代码。如果没有对应的处理代码程序将直接终止。
类型 runtime_error 是标准库异常类型的一种,定义在 stdexcept 头文件中。我们必须初始化 runtime_error 的对象,方式是给它提供一个 string 对象或者一个 c 风格的字符串,这个字符串可以是一些关于该异常的辅助信息。
try 语句块
try 语句块的通用语法形式是:
1 try{ 2 program-statements 3 }catch(exception-declaration){ 4 handler-statements 5 }catch(exception-declaration){ 6 handler-statements 7 }//...
try 语句块的一开始是关键字 try,随后紧跟着一个语句块,里面是可能出现异常的代码块。跟在 try 语句块之后的是一个或多个 catch 子句。catch 子句也包含三部分:关键字 catch,括号内一个(可能未命名的)对象的声明(异常声明)以及一个语句块。当选中了某个 catch 子句处理异常之后,执行与之对应的块。 catch 一旦完成,程序跳转到 try 语句块最后一个 catch 子句之后那条语句继续执行。
try 语句块中的 program-statements 组成程序的正常逻辑,像其他任何块一样,其中可以包含声明在内的任意 c++ 语句。一如既往,try 语句块内声明的变量在外部无法访问,且在 catch 子句内也无法访问。
1 int a, b; 2 while(cin >> a >> b){ 3 try{ 4 if(b == 0) throw runtime_error("b can't be 0"); 5 }catch(runtime_error err){ 6 cout << err.what()//err 的类型是 runtime_error,因此能推断what是runtime_error类的一个成员函数.每个标准库异常类都定义了名为what的成员函数,
这些函数没有参数,返回的是c风格字符串(const char*).其中runtime_error的what成员函数返回的是初始化一个具体对象时所用的string对象的副本 7 << " Try agin? Enter y or n" << endl; 8 // 如果b==0则输出: b can't be 0 9 // Try agin? Enter y or n 10 char c; 11 cin >> c; 12 if(!cin || c == 'n') break; 13 } 14 }
函数在寻找处理代码的过程中推出
在复杂系统中,程序在遇到抛出异常的代码前,其执行的路径可能已经经过了多个 try 语句块。例如,一个 try 语句块可能调用了包含另一个 try 语句块的函数,新的 try 语句块肯能调用了包含又一个 try 语句块的新函数,以此类推。
寻找处理代码的过程与函数调用链刚好相反。当异常被抛出时,首先搜索抛出该异常的函数。如果没找到匹配的 catch 子句,终止该函数,并在调用该函数的函数中继续寻找。如果还是没有找到匹配的 catch 子句,这个新的函数也被终止,继续搜索调用它的函数。以此类推,沿着程序的执行路径逐层回退,知道找到适当类型的 catch 子句为止。
如果还是没能找到任何匹配的 catch 子句,程序转到名为 terminate 的标准库函数。该函数的行为与系统有关,一般情况下,执行该函数将导致程序非正常推出。
1 #include <iostream> 2 #include <stdexcept> 3 using namespace std; 4 5 void gel(int x){ 6 try{ 7 if(x == 0) throw runtime_error("b can't be 0");//抛出该异常的函数中没有对应的catch语句块,因此这个异常将在调用这个函数的main函数中找处理的catch语句块 8 }catch(out_of_range gg){//这个catch语句块不是处理tuntime_eeor异常的 9 ; 10 } 11 } 12 13 int main(void){ 14 int a, b; 15 while(cin >> a >> b){ 16 try{ 17 gel(b); 18 }catch(runtime_error err){ 19 cout << err.what() 20 << " Try agin? Enter y or n" << endl; 21 char c; 22 cin >> c; 23 if(!cin || c == 'n') break; 24 } 25 } 26 return 0; 27 }
对于那些没有任何 try 语句块定义的异常,也按照类似的方式处理。如果一段程序没有 try 语句块且发生了异常,系统就会调用 terminate 函数并终止当前程序的执行(如上面的第一个例子中的代码)。
标准异常
c++标准库中定义了一组类,用于报告标准库函数遇到的问题。这些异常类也可以在用户编写的程序中使用,它们分别定义在4个头文件中:
1. exception 头文件定义了最通用的异常类 exception。它只报告异常的发生,不提供任何额外信息。
2. stdexcept 头文件定义了几种常用的异常类
3. new 头文件定义了 bad_alloc 异常类型
4. 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 逻辑错误:用一个超出有效范围的值
标准库异常类只定义了几种运算,包括创建或拷贝异常类型的对象,以及为异常类型的对象赋值。只能以默认初始化的方式初始化 exception, bad_alloc 和 bad_cast 对象,不允许为这些对象提供初始值。其他异常类型的行为则恰好相反:应该使用 string 对象或 c 风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。创建此类对象时,必须提供初始值,该初始值含有错误相关的信息。
异常类型只定义了一个名为 what 的成员函数,该函数没有任何参数,返回值是一个指向 c 风格的字符串的 const char*。该字符串的目的是提供一下关于异常的一些文本信息。what 函数返回的 c 风格字符串的内容与异常对象的类型有关。如果异常类型有一个字符串初始值,则 what 返回该字符串。对于无初始值的异常类型(如: exception等)来说,what 返回的内容是由编译器决定的。
附一个个人感觉写的比较好的异常处理的blog:https://www.cnblogs.com/MrYuan/p/4800257.html