360 likes | 574 Views
异常处理. C++ 异常处理基础: try、throw、catch 异常声明( exception specification) 意外异常( unexpected exception) 异常处理的作用 动态内存管理的异常处理 new auto_ptr 类. 程序运行发生异常. 程序运行中总难免发生错误 数组元素的下标超界、访问 NULL 指针 除数为0 动态内存分配 new 需要的存储空间太大 …… 引起这些异常情况的原因: 代码质量不高,存在 BUG 输入数据不符合要求 程序的算法设计时考虑不周到 ……. 我们总希望在发生异常情况时
E N D
异常处理 • C++异常处理基础:try、throw、catch • 异常声明(exception specification) • 意外异常(unexpected exception) • 异常处理的作用 • 动态内存管理的异常处理 • new • auto_ptr类
程序运行发生异常 • 程序运行中总难免发生错误 • 数组元素的下标超界、访问NULL指针 • 除数为0 • 动态内存分配new需要的存储空间太大 • …… • 引起这些异常情况的原因: • 代码质量不高,存在BUG • 输入数据不符合要求 • 程序的算法设计时考虑不周到 • ……. • 我们总希望在发生异常情况时 • 不只是简单地终止程序运行 • 能够反馈异常情况的信息:哪一段代码发生的、什么异常 • 能够对程序运行中已发生的事情做些处理:取消对输入文件的改动、释放已经申请的系统资源 • ……
通常的做法是:在预计会发生异常的地方,加入相应的代码,但这种做法并不总是适用的通常的做法是:在预计会发生异常的地方,加入相应的代码,但这种做法并不总是适用的 ……//对文件A进行了相关的操作 fun(arg, ……);//可能发生异常 …… • caller该如何知道fun(arg, ……)是否发生异常 • 没有发生异常,可以继续执行 • 发生异常,应该在结束程序运行前还原对文件A的操作 • fun(arg, ……)是别人已经开发好的代码 • fun(arg, ……)的编写者不知道其他人会如何使用这个函数 • fun(arg, ……)会出现在表达式中,通过返回值的方式区分是否发生异常 • 不符合编写程序的习惯 • 可能发生多种异常,通过返回值判断也很麻烦 • 需要一种手段 • 把异常与函数的接口分开,并且能够区分不同的异常 • 在函数体外捕获所发生的异常,并提供更多的异常信息
try、catch int main() { try { int n = 0; int m = 9 / n; cout << "ok" << endl; } catch (...) { cout << "execption" << endl; } return 0; }//VC6 输出 exception Dev, VS 则导致程序出错
int main() { try { int * p = NULL; * p = 1; cout << "ok" << endl; } catch (...) { cout << "execption" << endl; } return 0; } //VC6 输出 exception Dev, VS 则导致程序出错
try、throw、catch void printChar(char c) { bool isVisible = false; if (c>'0' && c<'9') isVisible = true; if (c>'a' && c<'z') isVisible = true; if (c>'A' && c<'Z') isVisible = true; if (isVisible == false) throw false;//抛出异常 cout<<c<<endl; return; } int main(){ for(int i=0;i<255;i++){ try{ printChar(i); }//抛出异常的被执行代码 catch(bool visible){//捕获异常 cout<<i<<" is not visible?" <<endl; } } return 0; } //输出一系列非字母数字的Ascii码
throw:表示发生异常,执行异常抛出的操作 throwexception • exception描述被抛出异常的信息,可以是 • 任何类型的数值: • 某个类的对象 • 条件表达式 • 结果:终止throwexception所在try块的执行,直接执行try块后的catch块 • try:声明在一个程序块中可能会发生异常,该程序块之后需要一个或多个catch块对异常情况进行处理 try {//可能发生异常的程序块 …… }
catch:捕获try块抛出的一种异常,并对异常情况进行相应的处理catch:捕获try块抛出的一种异常,并对异常情况进行相应的处理 catch (exception-type argument){//对该类异常进行处理的代码 …… } • exception-type :所捕获异常的类型 • argument :可选项 • 用来接收throwexception中的exception • 传递异常抛出点的信息,在catch块中可以访问 • try块之后,必须紧跟至少一个catch块,否则产生语法错误。即使try块不会抛出异常也需要有对应的catch块 try { cout<<“in try block”<<endl; } cout<<“after try block\n”;//error: try block has no catch handlers ……
throw出现在try块内 class base{ string msg; public: void what() {cout<<"base object:"<<msg<<endl; return;} base(string s); }; base::base(string s){ msg = s; } …… try { throwbase(“XXX”);//创建一个异常对象 cout<<“in try block\n”; //没有执行 } catch (base obj) { obj.what(); } cout<<“after catch\n”; • 输出结果 base object:XXX after catch
throw出现在try块所调用的函数内 void fun(){ cout<<“before throw\n”; throwbase(“YYY”);//创建一个异常对象 cout<<“after throw\n”;//没有执行 return; } …… try { fun(); cout<<“in try block\n”; //没有执行 } catch (base obj) { obj.what(); } cout<<“after catch\n”; • 输出结果 before throw base object:YYY after catch
try块之后,可以有多个连续的catch块,分别处理不同的异常try块之后,可以有多个连续的catch块,分别处理不同的异常 try { …… } catch (exception-type1 argument1){ ……} catch (exception-type2 argument2){ ……} …… catch (exception-typen argumentn){ ……} • 如果try执行过程中没有抛出任何异常,将直接跳到第一个不是catch的语句执行 try { cout<<“no exception\n”; } catch (exception-type1 argument1){ cout<<“exception 1”;} catch (exception-type2 argument2){cout<<“exception 1”;} catch (exception-typen argumentn){cout<<“exception 1”;} cout<<“after catch\n”; • 输出结果 no exception after catch
如果try块抛出异常,依次比较被抛出异常的类型与catch块中的异常类型如果try块抛出异常,依次比较被抛出异常的类型与catch块中的异常类型 • 执行第一个匹配成功的catch块,然后跳至第一个不是catch的语句执行 class base{ public: void what() {cout<<"base object"<<endl; return;} }; class son:public base{ public: void what(){cout<<"son object"<<endl; return;} }; class grandson:public son{ public: void what(){cout<<"grandson object"<<endl; return;} };
try{ throw son(); } catch (base obj) { obj.what(); } catch (son obj) { obj.what(); } catch (grandson obj) { obj.what(); } cout<<“after catch\n”; • 输出结果 base object after catch try{ throw son(); } catch (grandson obj) { obj.what(); } catch (son obj) { obj.what(); } catch (base obj) { obj.what(); } cout<<“after catch\n”; • 输出结果 son object after catch • 可以从基类派生异常类 • 当catch块捕获基类类型的异常时,也可以捕获派生类类型的异常
如果try块抛出异常,被抛出异常的类型与后面各catch块中的异常类型都不匹配,程序将异常终止如果try块抛出异常,被抛出异常的类型与后面各catch块中的异常类型都不匹配,程序将异常终止 try{ throw 1; } catch (base obj) { obj.what(); } catch (son obj) { obj.what(); } catch (grandson obj) { obj.what(); } cout<<“after catch\n”; //没有执行 程序异常终止
避免未处理异常导致的程序终止 • 捕获并处理任意类型的异常 try{ throw 1; } catch (base obj) { obj.what(); } catch (son obj) { obj.what(); } catch (grandson obj) { obj.what(); } catch (...) { cout<<“generic exception handler\n”; } cout<<“after catch\n”; • 输出结果 generic exception handler after catch • 在catch (…) 块中 • 不能获得异常类型的信息 • 不能通过访问throwexception中exception传递的信息
catch(…)要作为try块后面catch块列表中的最后一个异常处理器,否则其他的catch块永远也不会被执行catch(…)要作为try块后面catch块列表中的最后一个异常处理器,否则其他的catch块永远也不会被执行 try{ throwson(); }/{throw 1;}/{throwbase();}/ {throwgrandson();} catch (…) { cout<<“generic exception handler\n”; } catch (base obj) { obj.what(); } catch (son obj) { obj.what(); } catch (grandson obj) { obj.what(); } cout<<“after catch\n”; • 输出结果 generic exception handler after catch
异常参数的传递 • throwexception创建异常参数 • 异常参数被复制,复本被传递给catch块 • 完成catch块执行后 • 删除异常参数复本 • 删除throwexception创建的异常参数 class base{ string msg; public: base() {cout<<"base constructor"<<endl; return;} base(base& obj) {cout<<“duplicator"<<endl; return;} ~base() {cout<<"base destructor"<<endl; return;}; void what() {cout<<"base object"<<endl; return;} };
void fun(){ • throwbase(); • cout<<“after throw\n”; • return; • } • …… • try { • fun(); • cout<<“in try block\n”; • } • catch (base obj) { obj.what(); } • cout<<“after catch\n”; • 输出结果 • base constructor • duplicator • base object • base destructor • base destructor • after catch try { throwbase(); cout<<“in try block\n”; } catch (base obj) { obj.what(); } cout<<“after catch\n”; 输出结果 base constructor duplicator base object base destructor base destructor after catch
try { • throwbase(); • cout<<“in try block\n”; • } • catch (base& obj) { obj.what(); } • cout<<“after catch\n”; • 输出结果 • base constructor • base object • base destructor • after catch • catch块的异常参数可以是引用
异常捕获与函数调用 • 在一个函数体内发生的异常,如果没有被函数体内的catch块捕获、或者函数体内没有catch块,则 • 退出函数 • 返回函数的调用点 • 异常捕获、或者继续返回上级函数调用点 • 已返回到main()但异常仍然没有被捕获:异常终止程序运行
class base{ string msg; public: base(string s) { msg = s; cout<<"base constructor:"<<msg<<endl; return; }; void what() { cout<<"base object:"<<msg<<endl; return; } }; void funC(){ throw base("X"); cout << "func" << endl; return; } void funB(){funC(); cout << "funb" << endl; return; } void funA(){funB(); cout << "funa" << endl; return; }
int main() { • try { • funA(); • cout<<“in try block\n”; • } • catch (base obj) { obj.what(); } • cout<<“after catch\n”; • return 0; • } • 异常被funA()所在的调用程序捕获 • 输出结果 • base constructor: X • base object:X • after catch • 则输出结果: • base constructor: X • 然后异常终止 • This application has requested the Runtime to terminate it in an unusual way. • Please contact the application's support team for more information. • 若: • int main() { • funA(); • return 0; • }
由catch块再抛出异常 • catch块在捕获一个异常后,可以再将这个异常抛出,由上一级try块的相应catch块进行处理 try { try { throwbase(“X”); } catch (base obj) { cout<<“inner catch\n”; throw;//再抛出可没有参数, //抛出的仍然是前面的base(“X”) } cout<<“inner try\n”; } catch (base obj) { cout<<“caught again\n”; obj.what(); } cout<<“after catch\n”; • 输出结果 • base constructor:X • inner catch • caught again • base object: X • after catch
未捕获异常的处理 • 只有try-catch块能够捕获被抛出的异常 • 有两种异常不能够被捕获 • try块之外的代码抛出的异常 • try块内抛出的异常、但异常的类型与后续各catch块的异常类型均不符 • 对于未捕获的异常,系统自动调用函数terminate。函数terminate: • 返回类型为void • 无参数 • 缺省情况下调用函数abort() • 可以通过函数set_terminate指定terminate执行的函数 set_terminate(myTerminate) • myTerminate:用户自定义终止函数的名称,返回类型为void、无参数 • 当myTerminate的最后一个操作不是退出程序时,函数terminate执行完myTerminate的函数体后自动调用abort()终止程序
void myTerminate() { cerr<<"myTerminate is called!\n"; exit(0); return; } void fun() { cout<<“throw an exception\n”; throw 5; return; } • int main() • { • try { • fun(); • } • catch(string obj) • { • cout<<obj; • }; • } • 异常退出,输出: • throw an exception • set_terminate(myTerminate); • try { fun(); } • catch(string obj){cout<<obj;}; • 正常退出,输出: • throw an exception • myTerminate is called! • set_terminate(myTerminate); • fun(); • 正常退出,输出: • throw an exception • myTerminate is called!
为什么要进行异常处理 • 未抛出的异常产生的后果 • 输入一个学生的信息,其中课程A的成绩因人为错误为-80 • 编写程序时,需要根据常识对检查输入数据的合法性,对非法错误抛出异常 • 未捕获的异常导致应用程序终止 • 输入的文件数据太大,导致内存申请失败 • 中间结果可能已经破坏原始的文件输入数据 • 最终的结果又未产生 • 需要通过捕获异常,根据异常的类型确定如何从中间结果恢复原始的数据
new操作与异常处理 • 在程序运行中,使用new进行动态内存分配时,如果分配失败将抛出bad_alloc异常 • 需要用catch捕获异常,并编写相应的处理代码 • 可以通过函数函数set_new_handler指定new异常时的处理策略 set_new_handler(myNewHandler) • myNewHandler :用户自定义的new异常处理函数的名称,返回类型为void、无参数 • 应该有终止程序运行的操作abort或exit • 执行该语句后,当发生new异常时不在抛出bad_alloc异常,而是执行myNewHandler
auto_ptr类与动态内存管理 • auto_ptr是一个类模板 在 • 维护动态分配内存的指针 • 提供运算符*、-> • 当auto_ptr对象被释放时,将自动对所维护的指针执行delete操作
#include <iostream> #include <memory> using namespace std; class myClass { public: ~myClass() { cout << "destructor myclass" << endl; } }; int main() { std::auto_ptr<myClass> ptr( new myClass ); return 0; } 输出结果: destructor myclass
当一个函数体抛出异常后,如果异常得到处理,将释放全部的局部变量 • 指针型变量:指针所指向的内存空间不会自动被释放 • 对每个指针型变量,分别用一个auto_ptr对象代替 • 释放auto_ptr对象将引起对指针所指向的内存空间的delete操作
class myClass{ string msg; public: void setMsg(string s) { msg=s; return; } void printMsg() {cout<<msg <<endl; return; }; ~myClass () {cout<<“myClass destructor "<<endl;}; }; void funA() { auto_ptr<myClass> ptr( new myClass ); ptr->setMsg(“hello from myClass!”); (*ptr).printMsg(); throw string(“exception thrown from function A”); return; } void funB() { myClass *ptr = new myClass; ptr->setMsg(“hello from myClass!”); (*ptr).printMsg(); throw string(“exception thrown from function B”); return; }
try { funA(); } catch (string s) { cout<<s<<endl; } cout<<“*********************\n”; try { funB(); } catch (string s) { cout<<s<<endl; } cout<<“after catch\n”; • 输出结果 hello from myClass! myClass destructor exception thrown from function A! ********************* hello from myClass! exception thrown from function B! after catch
auto_ptr对象不能够维护指向数组的指针 void funC() { auto_ptr<myClass> ptrA( new myClass[8] ); myClass *ptrB = new myClass[8] ; string s=“hello from object ”; int len = s.length() –1 ; for( int i=0; i<8; i++) { s[len]=‘0’+i; (ptrA+i)->setMsg(s);// error: operator ‘+’ is not defined (ptrB+i)->setMsg(s); } for( int i=0; i<8; i++) (ptrB+i)->print(); throw string(“exception thrown from function C”); return; } • 通过auto_ptr对象无法对数组元素进行下标定位
try { funC() } catch (string s) { cout<<s<<endl; } cout<<“after catch\n”; • 异常结束,输出结果 hello from object 0 hello from object 1 hello from object 2 hello from object 3 hello from object 4 hello from object 5 hello from object 5 hello from object 6 myClass destructor • ptrA只释放了一个 myClass对象