异常处理 try catch throw(C++)
C++中异常处理的语法。
关键字:1、 try 2、 catch 3、 throw
其中关键字try表示定义一个受到监控、受到保护的程序代码块;关键字catch与try遥相呼应,定义当try block(受监控的程序块)出现异常时,错误处理的程序模块处理异常,并且每个catch block都带一个参数(类似于函数定义时的数那样),这个参数的数据类型用于异常对象的数据类型进行匹配;而throw则是检测到一个异常错误发生后向外抛出一个异常事件,通知对应的catch程序块执行对应的错误处理。
1、还是给一个例子吧!如下:
1 int main() 2 { 3 cout << "In main." << endl; 4 //定义一个try block,它是用一对花括号{}所括起来的块作用域的代码块 5 try 6 { 7 cout << "在 try block 中, 准备抛出一个异常." << endl; 8 //这里抛出一个异常(其中异常对象的数据类型是int,值为1) 9 //由于在try block中的代码是受到监控保护的,所以抛出异常后,程序的 10 //控制流便转到随后的catch block中 11 throw 1; 12 cout << "在 try block 中, 由于前面抛出了一个异常,因此这里的代码是不会得以执行到的" << endl; 13 } 14 //这里必须相对应地,至少定义一个catch block,同样它也是用花括号括起来的 15 catch(int& value) 16 { 17 cout << "在 catch block 中, 处理异常错误。异常对象value的值为:"<< value << endl; 18 } 19 cout << "Back in main. Execution resumes here." << endl; 20 return 0; 21 }
2、语法很简单吧!的确如此。另外一个try block可以有多个对应的catch block,可为什么要多个catch block呢?这是因为每个catch block匹配一种类型的异常错误对象的处理,多个catch block呢就可以针对不同的异常错误类型分别处理。毕竟异常错误也是分级别的呀!有致命的、有一般的、有警告的,甚至还有的只是事件通知。例子如下:
1 #include <iostream> 2 using namespace std; 3 4 int main() { 5 try 6 { 7 cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl; 8 throw 1; 9 cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl; 10 throw 0.5; 11 } 12 catch( int& value ) 13 { 14 cout << "在 catch block 中, int数据类型处理异常错误。" << endl; 15 } 16 catch( double& d_value ) 17 { 18 cout << "在 catch block 中, double数据类型处理异常错误。" << endl; 19 } 20 cin.get(); 21 return 0; 22 }
3、一个函数中可以有多个try catch结构块,例子如下:
1 #include <iostream> 2 using namespace std; 3 4 int main() { 5 try 6 { 7 cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl; 8 throw 1; 9 } 10 catch( int& value ) 11 { 12 cout << "在 catch block 中, int数据类型处理异常错误。" << endl; 13 } 14 //这里是二个trycatch结构块,当然也可以有第三、第四个,甚至更多 15 try 16 { 17 cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl; 18 throw 0.5; 19 } 20 catch( double& d_value ) 21 { 22 cout << "在 catch block 中, double数据类型处理异常错误。" << endl; 23 } 24 cin.get(); 25 return 0; 26 }
4、上面提到一个try block可以有多个对应的catch block(try 后必须紧跟着出现catch),这样便于不同的异常错误分类处理,其实这只是异常错误分类处理的方法之一(暂且把它叫做横向展开的吧!)。另外还有一种就是纵向的,也即是分层的、try catch块是可以嵌套的,当在低层的try catch结构块中不能匹配到相同类型的catch block时,它就会到上层的try catch块中去寻找匹配到正确的catch block异常处理模块。例程如下:
1 #include <iostream> 2 using namespace std; 3 4 int main() { 5 try 6 { 7 // 这里是嵌套的trycatch结构块,try catch 必须配对 8 try 9 { 10 cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl; 11 throw 1; 12 } 13 // int a = 10; // catch必须紧跟着try出现,如果之间加上语句,就会出错 14 catch( int &value ) 15 { 16 cout << "在 catch block 中, int数据类型处理异常错误。" << endl; 17 } 18 cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl; 19 throw 0.5; 20 } 21 catch( double d_value ) 22 { 23 cout << "在 catch block 中, double数据类型处理异常错误。" << endl; 24 } 25 cin.get(); 26 return 0; 27 }
5、讲到try catch块是可以嵌套分层的,并且通过异常对象的数据类型来进行匹配,以找到正确的catch block异常错误处理代码。这里就不得不详细叙述一下通过异常对象的数据类型来进行匹配找到正确的catch block的过程。
(1)首先在抛出异常的try catch块中查找catch block,按顺序先是与第一个catch block块匹配,如果抛出的异常对象的数据类型与catch block中传入的异常对象的临时变量(就是catch语句后面参数)的数据类型完全相同,或是它的子类型对象,则匹配成功,进入到catch block中执行;否则到二步;
(2)如果有2个或更多的catch block,则继续查找匹配第2个、第3个,乃至最后一个catch block,如匹配成功,则进入到对应的catch block中执行;否则到三步;
(3)返回到上一级的try catch块中,按规则继续查找对应的catch block。如果找到,进入到对应的catch block中执行;否则到四步;
(4)再到上上级的tryc atch块中,如此不断递归,直到匹配到顶级的try catch块中的最后一个catch block,如果找到,进入到对应的catch block中执行;否则程序将会执行terminate()退出。
1 #include <iostream> 2 using namespace std; 3 void Func() 4 { 5 // 这里实际上也是嵌套在里层的trycatch结构块 6 try 7 { 8 cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl; 9 // 由于这个trycatch块中不能找到匹配的catch block,所以 10 // 它会继续查找到调用这个函数的上层函数的try catch块。 11 throw 1; 12 } 13 catch(float &value) 14 { 15 cout << "在 catch block 中, float数据类型处理异常错误。" << endl; 16 } 17 } 18 int main() { 19 try 20 { 21 Func(); 22 cout << "在try block中,准备抛出一个double数据类型的异常.但是不会执行到这的" << endl; 23 throw 0.5; 24 } 25 catch(double &d_value) 26 { 27 cout << "在 catch block 中, double数据类型处理异常错误。" << endl; 28 } 29 catch(int &value) 30 { 31 // 这个例子中,Func()函数中抛出的异常会在此被处理 32 cout << "在 catch block 中, int数据类型处理异常错误。" << endl; 33 } 34 cin.get(); 35 return 0; 36 }
6、刚才提到,嵌套的try catch块是可以跨越函数作用域的,其实这里面还有另外一层涵义,就是抛出异常对象的函数中并不一定必须存在try catch块,它可以是调用这个函数的上层函数中存在try catch块,这样这个函数的代码也同样是受保护、受监控的代码;当然即便是上层调用函数不存在try catch块,也只是不能找到处理这类异常对象错误处理的catch block而已,例程如下:
1 #include <iostream> 2 using namespace std; 3 4 void Func() 5 { 6 // 这里实际上也是嵌套在里层的try catch结构块 7 // 由于这个函数中是没有try catch块的,所以它会查找到调用这个函数的上 8 // 层函数的trycatch块中。 9 throw 1; 10 } 11 12 int main() { 13 try 14 { 15 // 调用函数,注意这个函数里面抛出一个异常对象 16 Func(); 17 cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl; 18 throw 0.5; 19 } 20 catch(double &d_value) 21 { 22 cout << "在 catch block 中, double数据类型处理异常错误。" << endl; 23 } 24 catch(int &value) 25 { 26 // 这个例子中,Func()函数中抛出的异常会在此被处理 27 cout << "在 catch block 中, int数据类型处理异常错误。" << endl; 28 } 29 // 如果这里调用这个函数,那么由于main()已经是调用栈的顶层函数,因此不能找 30 // 到对应的catch block,所以程序会执行terminate()退出。 31 Func(); 32 // [特别提示]:在C++标准中规定,可以在程序任何地方throw一个异常对象, 33 // 并不要求一定只能是在受到try block监控保护的作用域中才能抛出异常,但 34 // 如果在程序中出现了抛出的找不到对应catch block的异常对象时,C++标 35 // 准中规定要求系统必须执行terminate()来终止程序。 36 // 因此这个例程是可以编译通过的,但运行时却会异常终止。这往往给软件 37 // 系统带来了不安全性。与此形成对比的是java中提供的异常处理模型却是不 38 // 允许出现这样的找不到对应catch block的异常对象,它在编译时就给出错误 39 // 提示,所以java中提供的异常处理模型往往比C++要更完善 40 cin.get(); 41 return 0; 42 }