基本概念
异常处理机制是将异常检测与异常处理进行相隔离,程序一部分负责异常检测,另一部分负责异常处理,符合高内聚低耦合的程序设计,异常检测和异常处理由信息携带异常信息的异常类进行通信。
C++语言中针对异常处理提供了throw 和try...catch语句块两种语法,通过这两个语法实现机制组合来实现异常的处理。
抛出异常:
异常检测部分通过抛出(throwing)一条表达式来引发(raised)一个异常,表示它遇到了异常。形式:
Throw 表达式;
表达式的类型就是异常的类型,异常捕捉严格按照类型匹配,它不允许相容类型的隐式转换。
栈展开
当执行一个throw时,跟着throw后面的语句不再执行,此时程序开始寻找catch处理模块,寻找处理代码的过程与函数调用过程刚好相反,寻找过程如下:
编译器首先寻找该异常的函数匹配的catch子句,如果没有则终止该函数,并在调用该函数的函数中继续寻找。如果函数没有找到与之匹配catch子句,这个新函数也被终止,继续寻找调用它的函数,以此类推,沿着程序的执行路径逐层回退,直到找到适当类型的catch子句,以上过程被称为栈展开(stack unwinding)。
如果最终还是没找到与之匹配的catch子句,程序将会转到terminate的标准库函数并终止当前程序的执行。
捕获异常:
当异常被抛出后,try...catch语句块就会捕获对应类型的异常,catch子句是按照顺序执行的, 最特殊的catch必须最先出现,否则永远执行不到,表达式如下:
try { //组成程序的正常逻辑,该程序可能会抛出异常 ... throw declaration;//异常抛出 ... } catch (exception_type1) { //异常类型1捕获和处理 ... } catch (exception_type2) { //异常类型2捕获和处理 ... } catch (exception_type3) { //异常类型3捕获和处理 ... }
应用1
//求两个数的商,并有异常处理功能 float CheckDivider(float x, float y) { try { if(y == 0) { //异常检测:抛出异常表示函数遇到异常,并在该某块处理 throw y; } return x / y; } catch (...) { cout << "error of dividing zero when running CheckDivider function" << endl; return -1.0f; } } //直接处理异常 void ThrowTest2() { float fResult = CheckDivider(2.5,0); cout << fResult << endl; //output: //error of dividing zero when running CheckDivider function //-1 }
应用2
//求两个数的商,仅抛出异常,无异常处理功能 float Divider(float x, float y) { if(y == 0) { //异常检测:抛出异常表示函数遇到异常 throw y; } return x / y; } //间接处理异常 void ThrowTest1() { float fResult = 0.0f; try { fResult = Divider(2,3); cout << "The result of x/y is : "<< fResult << endl; fResult = Divider(4,0);//底层有异常抛出 } catch(float)//捕获float类型 { //异常处理 cout << "error of dividing zero." << endl; } catch(...)//捕获所有类型的异常 { cout << "error when run" << endl; } //output: //The result of x/y is : 0.666667 //error of dividing zero. }
深入try ...catch
捕获所有异常的处理代码
为了一次性捕获所有异常,我们使用省略号作为异常声明,这样的处理代码称为捕获所有异常的处理代码,形如catch(…)。一条捕获所有异常处理的语句可以与任意类型的异常匹配。catch(…) 既能单独出现,也能与其他的几个catch语句一起出现。和其他catch语句一起出现时,catch(…)需要放在最后面。
函数try语句块
try函数块的应用场景一般局限于下面几个:
(1)构造函数初始化列表;
(2)基类构造含数据;
(3)析构函数
普通函数形式如下:
//函数try语句块 void ThrowTest()try { throw "ThrowTest3"; }catch(...) { cout << "throw error when run ThrowTest" <<endl; } //等价于 void ThrowTest()try { try { throw "ThrowTest3"; } catch(...) { cout << "throw error when run ThrowTest" <<endl; } }
深入throw
异常接口声明
1)为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型,
形如:void func() throw (A, B, C , D); //这个函数func()能够且只能抛出类型A B C D及其子类型的异常。
2)如果在函数声明中没有包含异常接口声明,则此函数可以抛掷任何类型的异常,
形如:void func();
3)一个不抛掷任何类型异常的函数可以声明为:
形如:void func() throw();
4) 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexpected函数会被调用,该函数默认行为调用terminate函数中止程序。
重新抛出
有时,一个单独的catch语句不能完整地处理某个异常,在执行某些校正操作后,当前的catch可能会决定由调用链更上一个层的函数接着处理异常。一个catch语句通过重新抛出(rethrowing)异常的操作将异常传递给另外一个catch语句。
形如:throw;
重新抛出不包含任何表达式,是指将当前的异常对象沿着调用链向上传递,并且只有当catch异常声明是引用类型时我们对参数所做的改变才会被保留和继续传播。空的throw语句只能出现在catch语句或者catch语句直接或者间接调用的函数内,否则会编译器将会调用terminate。
注意:在异常处理代码之外出现的throw;即使是catch(...)也不能捕捉到,这时进入终止函数
catch(Exception &eObj)//引用类型 { eObj.status = errCode::SevereErr//异常对象的status成员发生变化 throw;//异常对象的修改得以保留 } catch(Exception eObj)//非引用类型 { eObj.status = errCode::SevereErr//修改了异常对象的副本 throw;//异常对象status成员没有发生改变 }示例二
/**************************************************************** *函数名称:ReThrowTest3 *功 能:重新抛出后,异常被捕获 *作 者:Jin *日 期:2016年7月17日 ****************************************************************/ void ReThrowTest3() { try { try { throw "error"; } catch(...) { throw;//重新抛出 } } catch(...)//捕获字符串,输出打印 { cout << "throw error when run ThrowTest3" <<endl; } }示例3
/**************************************************************** *函数名称:ReThrowTest4 *功 能:重新抛出后,异常未被捕获 *作 者:Jin *日 期:2016年7月17日 ****************************************************************/ void ReThrowTest4() { try { throw;//非处理代码区域 ...也不能捕获异常,程序调用terminate } catch(...)//异常未被捕获,程序运行异常 { cout << "throw error when run ReThrowTest4" <<endl; } }
参考原文:
http://www.jb51.net/article/80913.htm
http://blog.csdn.net/zzjxiaozi/article/details/6649999