1 / 34

异常处理

异常处理. C++ 异常处理基础: try、throw、catch 异常声明( exception specification) 意外异常( unexpected exception) 异常处理的作用 动态内存管理的异常处理 new auto_ptr 类. 程序运行发生异常. 程序运行中总难免发生错误 数组元素的下标超界、访问 NULL 指针 除数为0 动态内存分配 new 需要的存储空间太大 …… 引起这些异常情况的原因: 代码质量不高,存在 BUG 输入数据不符合要求 程序的算法设计时考虑不周到 ……. 我们总希望在发生异常情况时

coye
Download Presentation

异常处理

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 异常处理 • C++异常处理基础:try、throw、catch • 异常声明(exception specification) • 意外异常(unexpected exception) • 异常处理的作用 • 动态内存管理的异常处理 • new • auto_ptr类

  2. 程序运行发生异常 • 程序运行中总难免发生错误 • 数组元素的下标超界、访问NULL指针 • 除数为0 • 动态内存分配new需要的存储空间太大 • …… • 引起这些异常情况的原因: • 代码质量不高,存在BUG • 输入数据不符合要求 • 程序的算法设计时考虑不周到 • ……. • 我们总希望在发生异常情况时 • 不只是简单地终止程序运行 • 能够反馈异常情况的信息:哪一段代码发生的、什么异常 • 能够对程序运行中已发生的事情做些处理:取消对输入文件的改动、释放已经申请的系统资源 • ……

  3. 通常的做法是:在预计会发生异常的地方,加入相应的代码,但这种做法并不总是适用的通常的做法是:在预计会发生异常的地方,加入相应的代码,但这种做法并不总是适用的 ……//对文件A进行了相关的操作 fun(arg, ……);//可能发生异常 …… • caller该如何知道fun(arg, ……)是否发生异常 • 没有发生异常,可以继续执行 • 发生异常,应该在结束程序运行前还原对文件A的操作 • fun(arg, ……)是别人已经开发好的代码 • fun(arg, ……)的编写者不知道其他人会如何使用这个函数 • fun(arg, ……)会出现在表达式中,通过返回值的方式区分是否发生异常 • 不符合编写程序的习惯 • 可能发生多种异常,通过返回值判断也很麻烦 • 需要一种手段 • 把异常与函数的接口分开,并且能够区分不同的异常 • 在函数体外捕获所发生的异常,并提供更多的异常信息

  4. 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 则导致程序出错

  5. int main() { try { int * p = NULL; * p = 1; cout << "ok" << endl; } catch (...) { cout << "execption" << endl; } return 0; } //VC6 输出 exception Dev, VS 则导致程序出错

  6. 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码

  7. throw:表示发生异常,执行异常抛出的操作 throwexception • exception描述被抛出异常的信息,可以是 • 任何类型的数值: • 某个类的对象 • 条件表达式 • 结果:终止throwexception所在try块的执行,直接执行try块后的catch块 • try:声明在一个程序块中可能会发生异常,该程序块之后需要一个或多个catch块对异常情况进行处理 try {//可能发生异常的程序块 …… }

  8. 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 ……

  9. 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

  10. 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

  11. 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

  12. 如果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;} };

  13. 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块捕获基类类型的异常时,也可以捕获派生类类型的异常

  14. 如果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”; //没有执行 程序异常终止

  15. 避免未处理异常导致的程序终止 • 捕获并处理任意类型的异常 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传递的信息

  16. 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

  17. 异常参数的传递 • 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;} };

  18. 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

  19. 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块的异常参数可以是引用

  20. 异常捕获与函数调用 • 在一个函数体内发生的异常,如果没有被函数体内的catch块捕获、或者函数体内没有catch块,则 • 退出函数 • 返回函数的调用点 • 异常捕获、或者继续返回上级函数调用点 • 已返回到main()但异常仍然没有被捕获:异常终止程序运行

  21. 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; }

  22. 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; • }

  23. 由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

  24. 未捕获异常的处理 • 只有try-catch块能够捕获被抛出的异常 • 有两种异常不能够被捕获 • try块之外的代码抛出的异常 • try块内抛出的异常、但异常的类型与后续各catch块的异常类型均不符 • 对于未捕获的异常,系统自动调用函数terminate。函数terminate: • 返回类型为void • 无参数 • 缺省情况下调用函数abort() • 可以通过函数set_terminate指定terminate执行的函数 set_terminate(myTerminate) • myTerminate:用户自定义终止函数的名称,返回类型为void、无参数 • 当myTerminate的最后一个操作不是退出程序时,函数terminate执行完myTerminate的函数体后自动调用abort()终止程序

  25. 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!

  26. 为什么要进行异常处理 • 未抛出的异常产生的后果 • 输入一个学生的信息,其中课程A的成绩因人为错误为-80 • 编写程序时,需要根据常识对检查输入数据的合法性,对非法错误抛出异常 • 未捕获的异常导致应用程序终止 • 输入的文件数据太大,导致内存申请失败 • 中间结果可能已经破坏原始的文件输入数据 • 最终的结果又未产生 • 需要通过捕获异常,根据异常的类型确定如何从中间结果恢复原始的数据

  27. new操作与异常处理 • 在程序运行中,使用new进行动态内存分配时,如果分配失败将抛出bad_alloc异常 • 需要用catch捕获异常,并编写相应的处理代码 • 可以通过函数函数set_new_handler指定new异常时的处理策略 set_new_handler(myNewHandler) • myNewHandler :用户自定义的new异常处理函数的名称,返回类型为void、无参数 • 应该有终止程序运行的操作abort或exit • 执行该语句后,当发生new异常时不在抛出bad_alloc异常,而是执行myNewHandler

  28. auto_ptr类与动态内存管理 • auto_ptr是一个类模板 在 • 维护动态分配内存的指针 • 提供运算符*、-> • 当auto_ptr对象被释放时,将自动对所维护的指针执行delete操作

  29. #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

  30. 当一个函数体抛出异常后,如果异常得到处理,将释放全部的局部变量 • 指针型变量:指针所指向的内存空间不会自动被释放 • 对每个指针型变量,分别用一个auto_ptr对象代替 • 释放auto_ptr对象将引起对指针所指向的内存空间的delete操作

  31. 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; }

  32. 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

  33. 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对象无法对数组元素进行下标定位

  34. 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对象

More Related