《C++异常处理的编程方法.docx》由会员分享,可在线阅读,更多相关《C++异常处理的编程方法.docx(216页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、C+异常处理的编程方法主人公介绍阿愚,曾经是一位小小程序员,现在仍是一位小小程序员,将来也还是一位小小程序员。读程序、写 程序 宜就是他的最爱,这过程给他带来许许多多的快乐,虽然期间也有过些迷茫,但每次冲过迷雾, 重见阳光的喜悦总是他对程序人生的更加执著追求。雨过天晴的空气是最清新的。异常处理的编程方法,程序员都很熟悉的个东东,她和面向对象的方法是软件程序设计发展史I:其 中最重要的两项革新技术。现代程序设计语言拥有的一个重要的特性就是能较好地支持异常的处理(Exception Handling),她就像一位美丽而优雅的公主,帮助程序员写出来的代码总是那样的整齐美观、 层次淸晰;同时它好像还是
2、一位贤惠能干的贤内助,总能帮你料理好由于考虑不全所留的多多少少的意 外事件,她在背后默默的支持你的一切,使你写出来的作品是那样的髙效、安全和完美。瞧!它深深地打 动了我们我们的主人公阿愚,并续上了 段美丽的编程爱情故事。内容的组织及编排相遇篇第1集初次与异常处理编程相邂逅第2集C+中异常处理的游戏规则第3集C+中catch (.)如何使用第4集C+的异常处理和面向对象的紧密关系第5集C+的异常rethrow相知篇第6集对象的成员函数中抛出的异常第7集构造函数中抛出的异常第8集 析构函数中抛出的异常第9集C+的异常对象如何传送第10集C+的异常对象按传值的方式被传递第11集C+的异常对象按引用方
3、式被传递第12集C+的异常对象按指针方式被传递第13集C+异常对象三种方式传递的综合比较第14集再探C+中异常的rethrow第15集C语言中的异常处理机制第16集C语言中一种更优雅的异常处理机制第1?集 全面了解setjmp与ongjmp的使用第18集 玩转setjmp与!ongjmp)第!9集setjmp与!ongjmp机制,很难与C+和睦相处第20集C+中如何兼容并支持C语言中提供的异常处理机制第21集Windows系列操作系统平台中的提供的异常处理机制第22集 更进步认识SEH第23集SEH的强大功能之一第24集SEH的强大功能之第25集SEH的综合第26集SEH可以在C+程序中使用第
4、27集SEH与C+异常模型的混合使用第28集Java中的异常处理模型第29集Unix操作系统提供中的异常处理机制相爱篇让异常成为函数接口的一部分异常能够优雅地跨越组件C+标准库中的异常分类模型MFC类库中的异常分类模型JDK平台中的异常分类模型爱的秘密实现爱的结晶对现有模型的些完善与改进第1集初次与异常处理编程相邂逅和其它很多程序员样,本书的主人公阿愚也是在初学C+时,在C+的sample代码中与异常处理的 编程方法初次邂逅的,如下:/ Normal program statements try(/ Execute some code that might throw an exception
5、.catch( CException* e)(/ Handle the exception here./ e contains information about the exception.e-Delete();/ Other normal program statements瞧瞧,代码看上去显得那么整齐、干净,try block和catch block遥相呼应,多有对称美呀!因此主人 公初次见面后就一见钟情了。为什么要选用异常处理的编程方法?当然更为重要的是,C+中引入的异常处理的编程机制提供给程序员种全新的、更好的编程方法和 思想。在C+中明确提出tiycatch异常处理编程方法的框架之
6、前的年代,程序员是怎样编写程序的,如下:void main(int argc, char* argv) (if (Call_Func l(in, param out) (/Z函数调用成功,我们正常的处理 if (Call_Func2(in, param out) /Z函数调用成功,我们正常的处理 while(condition)(/do other job if (has error) (/Z函数调用失败,表明程序执行过程中出现些错误, /Z因此必须处理错误 process_error();exit();)/do other job ) )else ( /Z函数调用失败,表明程序执行过程中出现
7、些错误, /Z因此必须处理错误 process_error();exit();) else(/Z函数调用失败,同样是错误处理 process_error();exit();因为程序的执行过程中总会遇到许多可预知或不可预知的错误事件,例如说,由于内存资源有限导致 需耍分配的内存失败了:或某个冃录下本应存在的个文件找不着了:或说不小心被零除了、内存越界了、 数组越界了等等。这些错误事件存在非常大的隐患,因此程序员总需要在程序中不断加入if语句,来判断 是否有异常出现,如果有,就必须要及时处理,否则可能带来意想不到的,甚至是灾难性的后果。这样 来,程序可读性差了很多,总是有许多与真正工作无关的代码,
8、而且也给程序员增加了极大的工作负担, 多数类似的处理错误的代码模块就像满出的牛屎样遍地都是(程序员不大多是牛人吗?所以。哈 哈)。但C+中的异常处理的机制彻底改变了这种面貌,它使真正的计算处理和错误处理分开来,让程序员 不再被这些琐碎的事情所烦扰,能关注丁真正的计算处理工作。同时代码的可读性也好了。因此我们有理 由选择异常处理的编程方法。具体原因如:1、把错误处理和真正的工作分开来:2、代码更易组织,更淸晰,复杂的工作任务更容易实现:3、毫无疑问,更安全了,不至于由于些小的疏忽而使程序意外崩溃了:4、由于C+中的try catch可以分层嵌套,所以它提供了一种方法使得程序的控制流可以安全的跳转
9、 到上层(或者上上层)的错误处理模块中去。(不同于return语句,异常处理的控制流是可以安全地跨越 个或多个函数)。5、还有一个重要的原因就是,由于目前需要开发的软件产品总是变得越来越复杂、越来越庞大,如 果系统中没有一个可靠的异常处理模型,那必定是一件十分糟糕的局面。相信绝大多数程序员都知道C+中的异常处理的编程方法,可还是有很多人已习惯原来单纯的面向过 程的代码组织方式,不太习惯或较少使用uycatch异常处理。为了使您编写的代码更安全:为了使您编写 的代码让他人更易阅读,主大公阿愚强烈建议在您书写的代码中尽可能多用异常处理机制,少些不必要 的if判断语句。下一集详细介绍C+中的异常处理
10、的语法。第2集C+中异常处理的游戏规则如果您喜欢玩一款游戏,您必须先要很好理解这款游戏的规则。同样主人公阿愚喜欢上C+中异常处 理后,当然也首先关注它的游戏规则,这就是C+中异常处理的语法。1、2、3、关键字trycatchthrow其中关键字try表示定义一个受到监控、受到保护的程序代码块:关键字catch与try遥相呼应,定义 当try block (受监控的程序块)出现异常时,错误处理的程序模块,并且每个catch block都带一个参数(类 似于函数定义时的数那样),这个参数的数据类型用于异常对象的数据类型进行匹配;而throw则是检测 到个异常错误发生后向外抛出个异常事件,通知对应的
11、cach程序块执行对应的错误处理。语法1、还是给个例子吧!如下:int main()(cout In main. endl;定义个tryblock,它是用对花括号所括起来的块作用域的代码块try ( cout 在try block中,准备抛出个异常. vv endl;这里抛出个异常(其中异常对象的数据类型是int,值为1)由于在try block中的代码是受到监控保护的,所以抛出异常后,程序的 控制流便转到随后的catch block中throw 1;cout”在try block中,由于前面抛出了 个异常,因此这里的代码是不会得以执行到的” endl;)这里必须相对应地,至少定义个catch
12、 block,同样它也是用花括号括起来的 catch( int& value)( coutvv”在catch block处理异常错误。异常对象value的值为:”vv value v endl;cout Back in main. Execution resumes here. endl;return 0;2、语法很简单吧!的确如此。另外一个try block可以有多个对应的catch block,可为什么要多个catch block呢?这是因为每个catch block匹配种类型的异常错误对象的处理,多个catch block呢就可以针对不 同的异常错误类型分别处理。毕竟异常错误也是分级别的
13、呀!有致命的、有一般的、有警告的,甚至还有 的只是事件通知。例子如下:int main()trycoul v ”在try block中,准备抛出个int数据类型的异常.” endl; throw 1;cout vv ”在try block中,准备抛出个double数据类型的异常. endl; throw 0.5;catch( int& value )(cout vv 在catch block中,int数据类型处理异常错误。( endl;)catch( double& d_value)(cout vv 在catch block中,double数据类型处理异常错误。vv endl;return 0
14、;3、个函数中可以有多个bycatch结构块,例子如下:int main()(try(cout v ”在try block中,准备抛出个int数据类型的异常.” endl; throw 1;)catch( int& value )(cout vv ”在catch block中,int数据类型处理异常错误。” endl;)这里是二个tiycatch结构块,当然也可以有第三、第四个,甚至更多try(cout vv 在try block中,准备抛出个double数据类型的异常.” v endl; throw ().5;)catch( double& d_value)cout vv ”在catch b
15、lock中,double数据类型处理异常错误。vv endl;) return 0;4、上面提到个try block可以有多个对应的calch block,这样便于不同的异常错误分类处理,其实这 只是异常错误分类处理的方法之(暂且把它叫做横向展开的吧!)。另外还有种就是纵向的,也即是 分层的、trycatch块是可以嵌套的,当在低层的trycatch结构块中不能匹配到相同类型的catch block时,它 就会到上层的trycatch块中去寻找匹配到正确的catch block异常处理模块。例程如下:int main()(try(这里是嵌套的trycatch结构块trycout vv ”在tr
16、y block中,准备抛出个int数据类型的异常.” v endl;throw 1;catch( int& value )(cout vv ”在catch block中,int数据类型处理异常错误。v endl;cout vv 在try block中,准备抛出个double数据类型的异常. endl;throw 0.5;catch( double& d_value)(coulvv”在catch block中,double数据类型处理异常错误。vv endl;return 0;5、讲到是trycatch块是可以嵌套分层的,并且通过异常对象的数据类型来进行匹配,以找到正确的catch block异
17、常错误处理代码。这里就不得不详细叙述一下通过异常对象的数据类型来进行匹配找到正确的catch block的过程。(1)首先在抛出异常的trycatch块中査找catch block,按顺序先是与第一个catch block块匹配,如果 抛出的异常对象的数据类型与catch block中传入的异常对象的临时变量(就是catch语句后面参数)的数 据类型完全相同,或是它的子类型对象,则匹配成功,进入到catch block中执行;否则到步;(2)如果有二个或更多的catch block,则继续査找匹配第二个、第三个,乃至最后个catch block, 如匹配成功,则进入到对应的catch bloc
18、k中执行;否则到三步;(3)返回到上一级的trycatch块中,按规则继续查找对应的catch block。如果找到,进入到对应的 catch block中执行;否则到四步;(4)再到上上级的trycalch块屮,如此不断递归,直到匹配到顶级的trycatch块中的最后个calch block如果找到,进入到对应的catch block中执行:否则程序将会执行terminate。退出。另外分层嵌套的trycatch块是可以跨越函数作用域的,例程如下:void Func() throw() (这里实际上也是嵌套在里层的trycatch结构块try ( cout v ”在try block中,准备
19、抛出个int数据类型的异常.” v endl;由于这个trycatch块中不能找到匹配的catch block,所以 它会继续査找到调用这个函数的上层函数的wycatch块。 throw 1;catch( float& value) (cout vv ”在catch block中,int数据类型处理异常错误。v endl;int main()(try ( Func();cout vv ”在try block中,准备抛出个double数据类型的异常.vv endl;throw 0.5;)catch( double& d_value)cout ft catch block中,double数据类型处
20、理异常错误。M endl;)catch( int& value ) 这个例子中,Func。函数中抛出的异常会ft此被处理cout ft catch block中,int数据类型处理异常错误。v endl; )return 0;)6、刚提到,嵌套的trycatch块是可以跨越函数作用域的,其实这里面还有另外一层涵义,就是抛出 异常对象的函数中并不定必须存ft trycatch块,它可以是调用这个函数的上层函数中存ft trycatch块,这 样这个函数的代码也同样是受保护、受监控的代码;当然即便是上层调用函数不存在irycatch块,也只是 不能找到处理这类异常对象错误处理的catch bloc
21、k而已,例程如下:void Func() throw()(这里实际上也是嵌套在里层的trycatch结构块由于这个函数屮是没有trycalch块的,所以它会査找到调用这个函数的上层函数的trycatch块中。throw 1;int main()(try(调用函数,注意这个函数里面抛出个异常对象 Func();cout v 在try block中,准备抛出个double数据类型的异常.vv endl;throw 0.5;catch( double& d_value)cout vv 在catch block中,double数据类型处理异常错误。vv endl;)catch( int& value)
22、(这个例子中,Func()函数中抛出的异常会在此被处理coutvv”在catch block中,int数据类型处理异常错误。v endl; 如果这里调用这个函数,那么由于main。已经是调用栈的顶层函数,因此不能找 到对应的catch block,所以程序会执行terminate。退出。Func。;特别提示:在C+标准中规定,可以在程序任何地方throw 一个异常对象, /并不要求一定只能是在受到iry block监控保护的作用域中才能抛出异常,但 /如果在程序中出现了抛出的找不到对应catch block的异常对象时,C+标 /Z准中规定要求系统必须执行terminate。来终止程序。/Z因
23、此这个例程是可以编译通过的,但运行时却会异常终止。这往往给软件/Z系统带来了不安全性。与此形成对比的是java中提供的异常处理模型却是不/Z永许出现这样的找不到对应catch block的异常对象,它在编译时就给出错误 提示,所以java中提供的异常处理模型往往比C+要更完善,后面的章节 /Z会进步对这两种异常处理模型进行个详细的分析比较。return 0;朋友们! C+中的异常处理模型的语法很简单吧!就是那么(one、two、three、哈哈!数数呢!) 简单的儿条规则。怪不得主人公阿愚这么快就喜欢上她了,而且还居然像个思想家样总结出一条感想: 好的东西往往都是简单的,简单就是美吗!哈哈!还
24、挺臭美的。下集主人公阿愚愿和大家一起讨论一下C+中的异常处理中的种特殊的catch用法,那就是关于 catch()大探秘。第3集C+中catch()如何使用上一篇文章中详细讲了讲C+异常处理模型的trycatch使用语法,其中catch关键字是用来定义catch block的,它后面带个参数,用来与异常对象的数据类型进行匹配。注意calch关键字只能定义个参数, 因此每个catch block只能是一种数据类型的异常对象的错误处理模块。如果要想使个catch block能抓获 多种数据类型的异常对象的话,怎么办? C+标准中定义了一种特殊的catch用法,那就是“ calchj.)”。感性认识
25、1、catch()到底是个什么样的东东,先来个感性认识吧!看例子先:int main()(trycout v ”在try block中,准备抛出个异常.vv endl;这里抛出个异常(其中异常对象的数据类型是int,值为1)throw 1;)/catch( int& value )注意这里catch语句catch( .)(coutvv在catch(.) block中,抛出的int类型的异常对象被处理 vv endl;2、哈哈! int类型的异常被catch()抓获了,再来另个例子:int main()try(coutvv 在try block中,准备抛出个异常.vv endl;这里抛出个异常(
26、其中异常对象的数据类型是double,值为0.5) throw 0.5;/catch( double& value)注意这里catch语句catch( .) coul v ”在catch(.) block中,double类型的异常对象也被处理vv endl;3、同样,double类型的异常对象也被catch()块抓获了。是的,catch(.)能匹配成功所有的数据类型 的异常对象,包括C+语言提供所有的原生数据类型的异常对象,如int、double,还有char、im这样的 指针类型,另外还有数组类型的异常对象。同时也包括所有自定义的抽象数据类型。例程如下:int main() try cout
27、 v ”在try block中,准备抛出个异常.vv endl;这里抛出个异常(其中异常对象的数据类型是char*)char* p=0;throw p; /catch( char* value )注意这里catch语句 catch( .) (coutcv”在catch(.) block中,char类型的异常对象也被处理vv endl;int main() cout vv ”在try block中,准备抛出个异常.v endl;这里抛出个异常(其中异常对象的数据类型是inti)int a4;throw a;/catch( int value)注意这里catch语句catch( .) cout v
28、v 在catch(.) block中,int口类型的异常对象也被处理 endl;4、对于抽象数据类型的异常对象。catch()同样有效,例程如:class MyException public:protected: int code;;int main()(trycout v ”在try block中,准备抛出个异常.v endl;这里抛出个异常(其中异常对象的数据类型是MyException) throw MyException();/catch( MyException& value)注意这里catch语句catch( .)(cout vv ”在 catch(.) block 中,MyEx
29、ception 类型的异常对象被处理vv endl; )对catch()有点迷糊?1、究竟对catch(.)有什么迷糊呢?还是看例子先吧!void main()(int* p = 0;try(注意:下面这条语句虽然不是throw语句,但它在执行时会导致系统/Z出现个存储保护错误的异常(access violation exception)*p = 13; / causes an access violation exception;)catch(.)(catch(.)能抓获住上面的 access violation exception 异常吗?cout DataType2 和 DataType
30、3 三 /Z种类型的异常対象在前面都已经有对应的calch block来处理。但为什么 /Z还要在最后再定义个catch()block呢?这就是为了有更好的安全性和 /Z可靠性,避免上面的iry block抛出了其它未考虑到的异常对象时导致的程 序出现意外崩溃的严重后果,而且这在用VC开发的系统上更特别有效,因 /Z为catch()能捕获系统出现的异常,而系统异常往往令程序员头痛了,现 /在系统般都比较复杂,而且由很多人共同开发,不小心就会导致个 /Z指针变量指向了其它非法区域,结果意外灾难不幸发生了。catch()为这种 /潜在的隐患提供了一种有效的补救措施。catch(.)还有,特别是VC
31、程序员为了使开发的系统有更好的可靠性,往往在应用程序的入口函数中(如MFC 框架的开发环境下CXXXApp:InitInstance()和工作线程的入口函数中加个顶层的trycatch块,并且使 用catch()来捕获一切所有的异常,如下:BOOL CXXXApp:InitInstance()(if (!AfxSocketInit()(AfxMessageBox(IDP_SOCKETS_INIT_FAILED);return FALSE;1AfxEnableControlContainerO;/ Standard initialization/ If you are not using the
32、se features and wish to reduce the size/ of your final executable, you should remove from the following/ the specific initialization routines you do not need.#ifdef_AFXDLLEnable3dControls(); / Call this when using MFC in a shared DLL#elseEnable3dControlsStatic(); / Call this when linking to MFC stat
33、ically#endif/注意这里有一个顶层的uycatch块,并且使用catch()来捕获一切所有的异常tryCXXXDlg dig;m_pMainWnd = &dlg:int nResponse = dlg.DoM(xlal();if (nResponse = IDOK)I/ TODO: Place code here to handle when the dialog is/ dismissed with OKelse if (nResponse = IDCANCEL)(/ TODO: Place code here to handle when the dialog is/ dismi
34、ssed with Cancel) ) catch(.) (/dump出系统的些重要信息,并通知管理员査找出现意外异常的原因。同时想办法恢复系统,例如说重新启动应用程序等)/ Since the dialog has been closed, return FALSE so that we exit the/ application, rather than start the applications message pump.return FALSE;)通过上面的例程和分析可以得出,由于catch()能够捕获所有数据类型的异常对象,所以在恰”的地 方使用catch()确实可以使软件系统有着
35、更好的可靠性。这确实是大家使用catch()这个东东最好的理由。 但不要误会的是,在C+异常处理模型中,不只有catch()方法能够捕获几乎所有类型的异常对象(也许 有其它更好的方法,在下篇文章中主人公阿愚带大家一同去探讨一下),可C+标准中为什么会想到定 义这样个catch()呢?有过java或C#编程开发经验的程序员会发现,在它们的异常处理模型中,并没有 这样类似的种语法,可这里不得不再次强调的是,java中的异常处理模型是C+中的异常处理模型的完 善改进版,可它反而没有了 catch。.),为何呢?还是先去看看章吧,“C+的异常处理和面向对象的紧 密关系也许大家能找到个似乎合理的原因。第
36、4集C+的异常处理和面向对象的紧密关系如果有人问起C+和C到底有那些本质上的不同点?主人公阿愚当然也会有自己的份理解,他会亳 不犹豫回答出:“与C相比,C+至少引入了两项重要技术,其一就是对面向对象的全面支持:还有一项就 是C+优良的异常处理模型”。足的,这两项技术对构建出个优良的可靠复杂的软件系统都太重要了。可 这两项技术之间又有何关系呢?常客观公正的说,它们之间的关系实在是太紧密了,两者相互支持和依 赖,是构建优良可靠复杂的软件系统最不可缺乏的两个东东。用对象来描述程序中出现的异常虽然前几篇文章的内容中列举的些小例子程序大多都是throw 些如int、double类型的异常,但程 序员朋友
37、都很熟悉,实际开发环境中所抛出的异常都是个个代表抽象数据类型的对象,如C+标准库中 的std:exception(), MFC开发库中Cexception等。用对象来描述的我们程序中的出现异常的类型和异常信 息是C+异常处理模型中最闪光之处,而且这特点直沿用到java语言的异常处理模型中。为什么要用对象来描述程序中出现的异常呢?这样做的优势何在?主人公阿愚不喜欢穷摆出什么大道 理,还是老办法,从具体的实例入手。由于异常有许许多多种类型,如有致命的错误、一般的错误、警告 或其它事件通知等,而且不同类型的异常有不同的处理方法,有的异常是不可恢复的,而有的异常是可以 恢复的(专业术语叫做“通人”吧!
38、哈哈,主人公阿愚有时也会来点文经绐的东西),所以程序员在开发系 统时就必须考虑把各种可能出现的异常进行分类,以便能够分别处理。下面为个应用系统设计出个对 异常进行分类的简单例子,如下:从上面的异常分类来看,它有明显的层次性和继承性,这恰恰和面向对象的继承思想如出辙,因此 用对象来描述程序中出现的异常是再恰当不过的了。而且可以利用面向对象的特性很好的对异常进行分类 处理,例如有这样个例子:void OpenFile(string f)try /Z打开文件的操作,可能抛出FileOpcnException)catch(FileOpenException& fe)(/Z处理这个异常,如果这个异常可以
39、很好的得以恢复,那么处理完毕后函数/正常返回;否则必须重新抛出这个异常,以供上层的调用函数来能再次处 /Z理这个异常对象int result = ReOpenFile;if (result = false) throw;void ReadFile(File f)(try/Z从文件中读数据,可能抛出FileReadException1catch(FileReadException& fe)(/Z处理这个异常,如果这个异常可以很好的得以恢复,那么处理完毕后函数/正常返回:否则必须重新抛出这个异常,以供上层的调用函数来能再次处/Z理这个异常对象int result = ReReadFile(f);i
40、f (result = false) throw;void WriteFile(File f)(try/往文件中写数据,可能抛出FileWriteExceptioncatch(FileWriteException& fe)/Z处理这个异常,如果这个异常可以很好的得以恢复,那么处理完毕后函数/正常返回;否则必须重新抛出这个异常,以供上层的调用函数来能再次处/Z理这个异常对象int result = ReWriteFile(f);if (result = false) throw;void Func() try ( 对文件进行操作,可能出现 FileWriteException、FileWriteException /Z 和 FileWriteException 异常OpenFile(.);ReadFile(.);WriteFile(.);)/ 注意:FileException 是 FileOpenException、FileReadException FileWriteException /Z的基类,因此这里定义的catch(FileException& fe)能捕获所有与文件操作失败的异 /Z常。catch(FileException& fe) (Exceptioninfo* ef = fe.