



版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
WB主人公介紹阿愚,曾經(jīng)是一位小小程序員,現(xiàn)在仍是一位小小程序員,將來也還是一位小小程序員。讀程序、寫程序一直就是他的最愛,這過程給他帶來許許多多的快樂,雖然期間也有過ー些迷茫,但每次沖過迷霧,重見陽光的喜悅總是他對程序人生的更加執(zhí)著追求。雨過天晴的空氣オ是最清新的。異常處理的編程方法,程序員都很熟悉的ー個東東,她和面向?qū)ο蟮姆椒ㄊ擒浖绦蛟O(shè)計(jì)發(fā)展史上其中最重要的兩項(xiàng)革新技術(shù)?,F(xiàn)代程序設(shè)計(jì)語言擁有的一個重要的特性就是能較好地支持異常的處理(ExceptionHandling)〇她就像一位美麗而優(yōu)雅的公主,幫助程序員寫出來的代碼總是那樣的整齊美觀、層次清晰;同時它好像還是一位賢惠能干的賢內(nèi)助,總能幫你料理好由于考慮不全所留下的多多少少的意外事件,她在背后默默的支持你的一切,使你寫出來的作品是那樣的高效、安全和完美。瞧!它深深地打動了我們的主人公阿愚,并續(xù)上了一段美麗的編程愛情故事。內(nèi)容的組織及編排相遇篇《第1集初次與異常處理編程相邂逅》《第2集C++中異常處理的游戲規(guī)則》《第3集C++中catch(…)如何使用》《第4集C++的異常處理和面向?qū)ο蟮木o密關(guān)系》《第5集C++的異常rethrow))相知篇《第6集對象的成員函數(shù)中拋出的異?!贰兜?集構(gòu)造函數(shù)中拋出的異?!贰兜?集析構(gòu)函數(shù)中拋出的異常》《第9集C++的異常對象如何傳送》《第10集C++的異常對象按傳值的方式被傳遞》《第11集C++的異常對象按引用方式被傳遞》《第12集C++的異常對象按指針方式被傳遞》《第13集C++異常對象三種方式傳遞的綜合比較》《第14集再探C++中異常的rethrow》《第15集C語言中的異常處理機(jī)制》《第16集C語言中一種更優(yōu)雅的異常處理機(jī)制》《第17集全面了解setjmp與longjmp的使用》《第18集玩轉(zhuǎn)setjmp與longjmp》《第19集setjmp與longjmp機(jī)制,很難與C++和睦相處》《第20集C++中如何兼容并支持C語言中提供的異常處理機(jī)制》《第21集Windows系列操作系統(tǒng)平臺中的提供的異常處理機(jī)制》《第22集更進(jìn)ー步認(rèn)識SEH》《第23集SEH的強(qiáng)大功能之一》《第24集SEH的強(qiáng)大功能之ニ》《第25集SEH的綜合》《第26集SEH可以在C++程序中使用》《第27集SEH與C++異常模型的混合使用》《第28集Java中的異常處理模型》《第29集Unix操作系統(tǒng)提供中的異常處理機(jī)制》相愛篇《讓異常成為函數(shù)接口的一部分》《異常能夠優(yōu)雅地跨越組件》《C++標(biāo)準(zhǔn)庫中的異常分類模型》《MFC類庫中的異常分類模型》《JDK平臺中的異常分類模型》愛的秘密《實(shí)現(xiàn)》愛的結(jié)晶《對現(xiàn)有模型的ー些完善與改進(jìn)》第1集初次與異常處理編程相邂逅和其它很多程序員ー樣,本書的主人公阿愚也是在初學(xué)C++時,在C++的sample代碼中與異常處理的編程方法初次邂逅的,如下://Normalprogramstatementstry(//Executesomecodethatmightthrowanexception.)catch(CException*e)(//Handletheexceptionhere.//"e"containsinformationabouttheexception.e->Delete();)//Othernormalprogramstatements瞧瞧,代碼看上去顯得那么整齊、干凈,tryblock和catchblock遙相呼應(yīng),多有對稱美呀!因此主人公初次見面后就一見鐘情了。為什么要選用異常處理的編程方法?當(dāng)然更為重要的是,C++中引入的異常處理的編程機(jī)制提供給程序員ー種全新的、更好的編程方法和思想。在C++中明確提出try/catch異常處理編程方法的框架之前的年代,程序員是怎樣編寫程序的,如下:voidmain(intargc,char*argv[])(if(Call_Funcl(in,paramout)(/Z函數(shù)調(diào)用成功,我們正常的處理if(Call_Func2(in,paramout)(/Z函數(shù)調(diào)用成功,我們正常的處理while(condition)(//dootherjobif(haserror)(/Z函數(shù)調(diào)用失敗,表明程序執(zhí)行過程中出現(xiàn)一些錯誤,/Z因此必須處理錯誤process_error();exit();//dootherjobelse{/Z函數(shù)調(diào)用失敗,表明程序執(zhí)行過程中出現(xiàn)一些錯誤,//因此必須處理錯誤process一error();exit();else(/Z函數(shù)調(diào)用失敗,同樣是錯誤處理process_error();exit();))因?yàn)槌绦虻膱?zhí)行過程中總會遇到許多可預(yù)知或不可預(yù)知的錯誤事件,例如說,由于內(nèi)存資源有限導(dǎo)致需要分配的內(nèi)存失敗了;或某個目錄下本應(yīng)存在的ー個文件找不著了;或說不小心被零除了、內(nèi)存越界了、數(shù)組越界了等等。這些錯誤事件存在非常大的隱患,因此程序員總需要在程序中不斷加入if語句,來判斷是否有異常出現(xiàn),如果有,就必須要及時處理,否則可能帶來意想不到的,甚至是災(zāi)難性的后果。這樣一來,程序可讀性差了很多,總是有許多與真正工作無關(guān)的代碼,而且也給程序員增加了極大的工作負(fù)擔(dān),多數(shù)類似的處理錯誤的代碼模塊就像滿山的牛屎ー樣遍地都是(程序員不大多是“牛”人嗎?所以。。。哈哈)。但C++中的異常處理的機(jī)制徹底改變了這種面貌,它使真正的計(jì)算處理和錯誤處理分開來,讓程序員不再被這些瑣碎的事情所煩擾,能關(guān)注于真正的計(jì)算處理工作。同時代碼的可讀性也好了。因此我們有理由選擇異常處理的編程方法。具體原因如下:1、把錯誤處理和真正的工作分開來;2、代碼更易組織,更清晰,復(fù)雜的工作任務(wù)更容易實(shí)現(xiàn);3、毫無疑問,更安全了,不至于由于ー些小的疏忽而使程序意外崩潰了;4、由于C++中的trycatch可以分層嵌套,所以它提供了一種方法使得程序的控制流可以安全的跳轉(zhuǎn)到上層(或者上上層)的錯誤處理模塊中去。(不同于return語句,異常處理的控制流是可以安全地跨越ー個或多個函數(shù))。5、還有一個重要的原因就是,由于目前需要開發(fā)的軟件產(chǎn)品總是變得越來越復(fù)雜、越來越龐大,如果系統(tǒng)中沒有一個可靠的異常處理模型,那必定是一件十分糟糕的局面。相信絕大多數(shù)程序員都知道C++中的異常處理的編程方法,可還是有很多人已習(xí)慣原來單純的面向過程的代碼組織方式,不太習(xí)慣或較少使用try/catch異常處理。為了使您編寫的代碼更安全;為了使您編寫的代碼讓他人更易閱讀,主人公阿愚強(qiáng)烈建議在您書寫的代碼中盡可能多用異常處理機(jī)制,少?些不必要的if判斷語句。下ー集詳細(xì)介紹C++中的異常處理的語法。第2集C++中異常處理的游戲規(guī)則如果您喜歡玩ー款游戲,您必須先要很好理解這款游戲的規(guī)則。同樣主人公阿愚喜歡上C++中異常處理后,當(dāng)然也首先關(guān)注它的游戲規(guī)則,這就是C++中異常處理的語法。關(guān)鍵字1、trycatchthrow其中關(guān)鍵字try表示定義一個受到監(jiān)控、受到保護(hù)的程序代碼塊:關(guān)鍵字catch與try遙相呼應(yīng),定義當(dāng)tryblock(受監(jiān)控的程序塊)出現(xiàn)異常時,錯誤處理的程序模塊,并且每個catchblock都帶ー個參數(shù)(類似于函數(shù)定義時的數(shù)那樣),這個參數(shù)的數(shù)據(jù)類型用于異常對象的數(shù)據(jù)類型進(jìn)行匹配;而throw則是檢測到ー個異常錯誤發(fā)生后向外拋出ー個異常事件,通知對應(yīng)的catch程序塊執(zhí)行對應(yīng)的錯誤處理。語法1、還是給ー個例子吧!如下:intmain()(cout?"Inmain."?endl;〃定義一個tryblock?它是用ー對花括號{}所括起來的塊作用域的代碼塊try(coutvv"在tryblock中,準(zhǔn)備拋出ー個異常.”vven進(jìn)〃這里拋出ー個異常(其中異常對象的數(shù)據(jù)類型是int,值為1)〃由于在tryblock中的代碼是受到監(jiān)控保護(hù)的,所以拋出異常后,程序的〃控制流便轉(zhuǎn)到隨后的catchblock中throw1;coutvv”在tryblock中,由于前面拋出了一個異常,因此這里的代碼是不會得以執(zhí)行到的”?endl;)〃這里必須相對應(yīng)地,至少定義ー個catchblock,同樣它也是用花括號括起來的catch(int&value)(cout?"在catchblock中,處理異常錯誤。異常對象value的值為:"vvvalue?endl;)cout?"Backinmain.Executionresumeshere."?endl;return0;2、語法很簡單吧!的確如此。另外一個tryblock可以有多個對應(yīng)的catchblock,可為什么要多個catchblock呢?這是因?yàn)槊總€catchblock匹配ー種類型的異常錯誤對象的處理,多個catchblock呢就可以針對不同的異常錯誤類型分別處理。畢竟異常錯誤也是分級別的呀!有致命的、有一般的、有警告的,甚至還有的只是事件通知。例子如下:intmain(){try(coutvv"在tryblock中,準(zhǔn)備拋出ー個int數(shù)據(jù)類型的異常."vvendl;throw1;coutvv”在tryblock中,準(zhǔn)備拋出ー個double數(shù)據(jù)類型的異常.""endl;throw0.5;)catch(int&value)(cout<<"在catchblock中,int數(shù)據(jù)類型處理異常錯誤。99?endl;}catch(double&d_value)(cout<<"在catchblock中,double數(shù)據(jù)類型處理異常錯誤。"<<endl;}return0;)3、ー個函數(shù)中可以有多個try/catch結(jié)構(gòu)塊,例子如下:intmain()(try(cout<<"ittryblock中,準(zhǔn)備拋出ー個int數(shù)據(jù)類型的異常."vvendl;throw1;)catch(int&value)(cout<<"在catchblock中,int數(shù)據(jù)類型處理異常錯誤。"<<endl;)〃這里是二個try/catch結(jié)構(gòu)塊,當(dāng)然也可以有第三、第四個,甚至更多try{coutvv”在tryblock中,準(zhǔn)備拋出ー個double數(shù)據(jù)類型的異常."vvendl;throw0.5;)catch(double&d_value)(cout?"在catchblock中,double數(shù)據(jù)類型處理異常錯誤。"<<endl;)return0;}4、上面提到ー個tryblock可以有多個對應(yīng)的catchblock,這樣便于不同的異常錯誤分類處理,其實(shí)這只是異常錯誤分類處理的方法之一(暫且把它叫做橫向展開的吧!)。另外還有一種就是縱向的,也即是分層的、try/catch塊是可以嵌套的,當(dāng)在低層的try/catch結(jié)構(gòu)塊中不能匹配到相同類型的catchblock時,它就會到上層的try/catch塊中去尋找匹配到正確的catchblock異常處理模塊。例程如下:intmain()(try(〃這里是嵌套的try/catch結(jié)構(gòu)塊try(coutvv”在tryblock中,準(zhǔn)備拋出ー個int數(shù)據(jù)類型的異常."vvendl;throw1;)catch(int&value)(cout?”在catchblock中,int數(shù)據(jù)類型處理異常錯誤。"?endl;coutvv”在tryblock中,準(zhǔn)備拋出ー個double數(shù)據(jù)類型的異常."vvendl;throw0.5;)catch(double&devalue)(cout?”在catchblock中,double數(shù)據(jù)類型處理異常錯誤。"?endl;}return0;)5、講到是try/catch塊是可以嵌套分層的,并且通過異常對象的數(shù)據(jù)類型來進(jìn)行匹配,以找到正確的catchblock異常錯誤處理代碼。這里就不得不詳細(xì)敘述一下通過異常對象的數(shù)據(jù)類型來進(jìn)行匹配找到正確的catchblock的過程。(1)首先在拋出異常的try/catch塊中查找catchblock,按順序先是與第一個catchblock塊匹配,如果拋出的異常對象的數(shù)據(jù)類型與catchblock中傳入的異常對象的臨時變量(就是catch語句后面參數(shù))的數(shù)據(jù)類型完全相同,或是它的子類型對象,則匹配成功,進(jìn)入到catchblock中執(zhí)行;否則到ニ步;(2)如果有二個或更多的catchblock,則繼續(xù)查找匹配第二個、第三個,乃至最后ー個catchblock,如匹配成功,則進(jìn)入到對應(yīng)的catchblock中執(zhí)行:否則到三步;(3)返回到上一級的try/catch塊中,按規(guī)則繼續(xù)查找對應(yīng)的catchblock。如果找至リ,進(jìn)入到對應(yīng)的catchblock中執(zhí)行;否則到四步;(4)再到上上級的try/catch塊中,如此不斷遞歸,直到匹配到頂級的try/catch塊中的最后一個catchblock,如果找到,進(jìn)入到對應(yīng)的catchblock中執(zhí)行;否則程序?qū)?zhí)行terminate。退出。另外分層嵌套的try/catch塊是可以跨越函數(shù)作用域的,例程如下:voidFunc。throw。(〃這里實(shí)際上也是嵌套在里層的try/catch結(jié)構(gòu)塊try(cout?”在tryblock中,準(zhǔn)備拋出ー個int數(shù)據(jù)類型的異常."vvendl;〃由于這個try/catch塊中不能找到匹配的catchblock,所以〃它會繼續(xù)查找到調(diào)用這個函數(shù)的上層函數(shù)的try/catch塊。throw1;}catch(float&value)(coutvv”在catchblock中,int數(shù)據(jù)類型處理異常錯誤。"vvendl;intmain。(try(Func。;cout?"在tryblock中,準(zhǔn)備拋岀ー個double數(shù)據(jù)類型的異常."。endl;throw0.5;)catch(double&d_value)(cout?"在catchblock中,double數(shù)據(jù)類型處理異常錯誤。"?endl;)catch(int&value){〃這個例子中,Func()函數(shù)中拋出的異常會在此被處理cout?"在catchblock中,int數(shù)據(jù)類型處理異常錯誤。"<<endl;)return0;6、剛オ提到,嵌套的try/catch塊是可以跨越函數(shù)作用域的,其實(shí)這里面還有另外一層涵義,就是拋出異常對象的函數(shù)中并不一定必須存在try/catch塊,它可以是調(diào)用這個函數(shù)的上層函數(shù)中存在try/catch塊,這樣這個函數(shù)的代碼也同樣是受保護(hù)、受監(jiān)控的代碼;當(dāng)然即便是上層調(diào)用函數(shù)不存在try/catch塊,也只是不能找到處理這類異常對象錯誤處理的catchblock而已,例程如下:voidFunc()throw()(〃這里實(shí)際上也是嵌套在里層的try/catch結(jié)構(gòu)塊〃由于這個函數(shù)中是沒有try/catch塊的,所以它會查找到調(diào)用這個函數(shù)的上〃層函數(shù)的try/catch塊中。throw1;)intmain(){try(〃調(diào)用函數(shù),注意這個函數(shù)里面拋出ー個異常對象Func();coutvv”在tryblock中,準(zhǔn)備拋出ー個double數(shù)據(jù)類型的異常."vvendl;throw0.5;}catch(double&devalue)(cout?"在catchblock中,double數(shù)據(jù)類型處理異常錯誤。"?endl;)catch(int&value){〃這個例子中,F(xiàn)unc()函數(shù)中拋出的異常會在此被處理cout?"在catchblock中,int數(shù)據(jù)類型處理異常錯誤。"?endl;)〃如果這里調(diào)用這個函數(shù),那么由于main()已經(jīng)是調(diào)用棧的頂層函數(shù),因此不能找〃到對應(yīng)的catchblock,所以程序會執(zhí)行terminate。退出。Func。;〃[特別提示]:在C++標(biāo)準(zhǔn)中規(guī)定,可以在程序任何地方throwー個異常對象,//并不要求一定只能是在受到tryblock監(jiān)控保護(hù)的作用域中才能拋出異常,但//如果在程序中出現(xiàn)了拋出的找不到對應(yīng)catchblock的異常對象時,C++標(biāo)/Z準(zhǔn)中規(guī)定要求系統(tǒng)必須執(zhí)行terminate。來終止程序。/Z因此這個例程是可以編譯通過的,但運(yùn)行時卻會異常終止。這往往給軟件/Z系統(tǒng)帶來了不安全性。與此形成對比的是java中提供的異常處理模型卻是不/Z永許出現(xiàn)這樣的找不到對應(yīng)catchblock的異常對象,它在編譯時就給出錯誤〃提示,所以java中提供的異常處理模型往往比C++要更完善,后面的章節(jié)/Z會進(jìn)ー步對這兩種異常處理模型進(jìn)行ー個詳細(xì)的分析比較。return0;)朋友們!C++中的異常處理模型的語法很簡單吧!就是那么(one、two、three、…哈哈!數(shù)數(shù)呢!)簡單的幾條規(guī)則。怪不得主人公阿愚這么快就喜歡上她了,而且還居然像ー個思想家?樣總結(jié)出一條感想:好的東西往往都是簡單的,簡單就是美嗎!哈哈!還挺臭美的。下ー集主人公阿愚愿和大家一起討論一下C++中的異常處理中的ー種特殊的catch用法,那就是關(guān)于catch(…)大探秘。第3集C++中catch(…)如何使用上一篇文章中詳細(xì)講了講C++異常處理模型的try/catch使用語法,其中catch關(guān)鍵字是用來定義catchblock的,它后面帶ー個參數(shù),用來與異常對象的數(shù)據(jù)類型進(jìn)行匹配。注意catch關(guān)鍵字只能定義ー個參數(shù),因此每個catchblock只能是ー種數(shù)據(jù)類型的異常對象的錯誤處理模塊。如果要想使ー個catchblock能抓獲多種數(shù)據(jù)類型的異常對象的話,怎么辦?C++標(biāo)準(zhǔn)中定義了一種特殊的catch用法,那就是"catch(???)"?感性認(rèn)識1、catch(…)到底是ー個什么樣的東東,先來個感性認(rèn)識吧!看例子先:intmain()(try(coutcc"在tryblock中,準(zhǔn)備拋出ー個異常."vvendl;〃這里拋出ー個異常(其中異常對象的數(shù)據(jù)類型是int,值為1)throw1;)//catch(int&value)〃注意這里catch語句catch(…)(cout?"在catch('")block中,拋出的int類型的異常對象被處理"<<endl;})2、哈哈!int類型的異常被catch(…)抓獲了,再來另ー個例子:intmain()(try(coutvv”在tryblock中,準(zhǔn)備拋出ー個異常.”<vendl;〃這里拋出ー個異常(其中異常對象的數(shù)據(jù)類型是double,值為0.5)throw0.5;)//catch(double&value)〃注意這里catch語句catch(…)(coutく〈”在catch()block中,double類型的異常對象也被處理”<vendl;3、同樣,double類型的異常對象也被catch(…)塊抓獲了。是的,catch(..)能匹配成功所有的數(shù)據(jù)類型的異常對象,包括C++語言提供所有的原生數(shù)據(jù)類型的異常對象,如int、double,還有char?、int?這樣的指針類型,另外還有數(shù)組類型的異常對象。同時也包括所有自定義的抽象數(shù)據(jù)類型。例程如下:intmain()(try(coutvc”在tryblock中,準(zhǔn)備拋出ー個異常."《endl;〃這里拋出ー個異常(其中異常對象的數(shù)據(jù)類型是char*)char*p=0;throwp;)//catch(char*value)〃注意這里catch語句catch(…)(coutv<”在catch(…)block中,char?類型的異常對象也被處理"<vendl;intmain()(try(cout?"ittryblock中,準(zhǔn)備拋出ー個異常."vvendl;〃這里拋出ー個異常(其中異常對象的數(shù)據(jù)類型是intロ)inta[4];throwa;1//catch(intvalue[])〃注意這里catch語句catch(…)(coutvv"在catch(,,e)block中,intU類型的異常對象也被處理"vvendl;1}4、對于抽象數(shù)據(jù)類型的異常對象。catch(…)同樣有效,例程如下:classMyExceptionpublic:protected:intcode;};intmain()(try(coutvv”在tryblock中,準(zhǔn)備拋出ー個異常."vvendl;〃這里拋出ー個異常(其中異常對象的數(shù)據(jù)類型是MyException)throwMyException();)//catch(MyException&value)〃注意這里catch語句catch(…)(cout?”在catch(…)block中,MyException類型的異常對象被處理”<vendl;對catch(…)有點(diǎn)迷糊?1、究竟對catch(…)有什么迷糊呢?還是看例子先吧!voidmain()(int*p=0;try(〃注意:下面這條語句雖然不是throw語句,但它在執(zhí)行時會導(dǎo)致系統(tǒng)/Z出現(xiàn)ー個存儲保護(hù)錯誤的異常(accessviolationexception)*p=13;//causesanaccessviolationexception;)catch(...)(〃catch(…)能抓獲住上面的accessviolationexception異常嗎?cout?”在catch(…)block中“?endl;1)請問上面的程序運(yùn)行時會出現(xiàn)什么結(jié)果嗎?catch(…)能抓獲住系統(tǒng)中出現(xiàn)的accessviolationexception異常嗎?朋友們!和我們的主人公阿愚ー樣,自己動手去測試一把!結(jié)果又如何呢?實(shí)際上它有兩種不同的運(yùn)行結(jié)果,在window2000系統(tǒng)下用VC來測試運(yùn)行這個小程序時,發(fā)現(xiàn)程序能輸出”在catch(…)block中”的語句在屏幕上,也即catch(…)能成功抓獲住系統(tǒng)中出現(xiàn)的accessviolationexception異常,很厲害吧!但如果這個同樣的程序在linux下用gcc編譯后運(yùn)行時,程序?qū)霈F(xiàn)崩潰,并在屏幕上輸出"segmentfaulビ的錯誤信息。主人公阿愚有點(diǎn)急了,也開始有點(diǎn)迷糊了,為什么?為什么?為什么同樣ー個程序在兩種不同的系統(tǒng)上有不同的表現(xiàn)呢?其原因就是:對于這種由于硬件或操作系統(tǒng)出現(xiàn)的系統(tǒng)異常(例如說被零除、內(nèi)存存儲控制異常、頁錯誤等等)時,window2000系統(tǒng)有一個叫做結(jié)構(gòu)化異常處理(StructuredExceptionHandling,SEH)的機(jī)制,這個東東太厲害了,它能和VC中的C++異常處理模型很好的結(jié)合上(實(shí)際上VC實(shí)現(xiàn)的C++異常處理模型很大程度上建立在SEH機(jī)制之上的,或者說它是SEH的擴(kuò)展,后面文章中會詳細(xì)闡述并分析這個久富盛名的SEH,看看catch(…)是如何神奇接管住這種系統(tǒng)異常出現(xiàn)后的程序控制流的,不過這都是后話)。而在!inux系統(tǒng)下,系統(tǒng)異常是由信號處理編程方法來控制的(信號處理編程,signalprocessingprogamming。在介紹unix和linux下如何編程的書籍中,都會有對信號處理編程詳細(xì)的介紹,當(dāng)然執(zhí)著的主人公阿愚肯定對它也不會放過,會深入到unix沿襲下來的信號處理編程內(nèi)部的實(shí)現(xiàn)機(jī)制,并嘗試完善改進(jìn)它,使它也能夠較好地和C++異常處理模型結(jié)合上)。那么C++標(biāo)準(zhǔn)中對于這種同一個程序有不同的運(yùn)行結(jié)果有何解釋呢?這里需要注意的是,window2000系統(tǒng)下catch(…)能捕獲住系統(tǒng)異常,這完全是它自己的擴(kuò)展。在C++標(biāo)準(zhǔn)中并沒有要求到這一點(diǎn),它只規(guī)定catch(…)必須能捕獲程序中所有通過throw語句拋出的異常。因此上面的這個程序在linux系統(tǒng)下的運(yùn)行結(jié)果也完全是符合C++標(biāo)準(zhǔn)的。雖然大家也必須承認(rèn)window2000系統(tǒng)下對C++異常處理模型的這種擴(kuò)展確實(shí)是一個很不錯的完善,極大得提高了程序的安全性。為什么要用catch(…)這個東東?程序員朋友們也許會說,這還有問嗎?這篇文章的ー開始不就講到了嗎?catch(…)能夠捕獲多種數(shù)據(jù)類型的異常對象,所以它提供給程序員ー種對異常對象更好的控制手段,使開發(fā)的軟件系統(tǒng)有很好的可靠性。因此ー個比較有經(jīng)驗(yàn)的程序員通常會這樣組織編寫它的代碼模塊,如下:voidFunc()(try(/Z這里的程序代碼完成真正復(fù)雜的計(jì)算工作,這些代碼在執(zhí)行過程中/Z有可能拋出DataTypel、DataType2和DataType3類型的異常對象。)catch(DataType1&dl)catch(DataType2&d2)catch(DataType3&d3)()//注意上面tryblock中可能拋出的DataTypel、DataType2和DataType3三種/Z類型的異常對象在前面都已經(jīng)有對應(yīng)的catchblock來處理。但為什么/Z還要在最后再定義ー個catch(…)block呢?這就是為了有更好的安全性和/Z可靠性,避免上面的tryblock拋出了其它未考慮到的異常對象時導(dǎo)致的程〃序出現(xiàn)意外崩潰的嚴(yán)重后果,而且這在用VC開發(fā)的系統(tǒng)上更特別有效,因〃為catch(…)能捕獲系統(tǒng)出現(xiàn)的異常,而系統(tǒng)異常往往令程序員頭痛了,現(xiàn)//在系統(tǒng)一般都比較復(fù)雜,而且由很多人共同開發(fā),ー不小心就會導(dǎo)致ー個〃指針變量指向了其它非法區(qū)域,結(jié)果意外災(zāi)難不幸發(fā)生了。catch(…)為這種〃潛在的隱患提供了一種有效的補(bǔ)救措施。catch(…)())還有,特別是VC程序員為了使開發(fā)的系統(tǒng)有更好的可靠性,往往在應(yīng)用程序的入口函數(shù)中(如MFC框架的開發(fā)環(huán)境下CXXXApp::InitInstance())和工作線程的入口函數(shù)中加上ー個頂層的try/catch塊,并且使用catch(…)來捕獲一切所有的異常,如下:BOOLCXXXApp::InitInstance()(if(!AfxSocketInit())(AfxMessageBox(IDP_SOCKETS_INIT_FAILED);returnFALSE;)AfxEnableControlContainer();//Standardinitialization//Ifyouarenotusingthesefeaturesandwishtoreducethesize//ofyourfinalexecutable,youshouldremovefromthefollowing//thespecificinitializationroutinesyoudonotneed.#ifdef_AFXDLLEnable3dControls();//CallthiswhenusingMFCinasharedDLL#elseEnable3dControlsStatic();//CallthiswhenlinkingtoMFCstatically#endif//注意這里有一個頂層的try/catch塊,并且使用catch(…)來捕獲一切所有的異常try(CXXXDlgdig;m_pMainWnd=&dlg;intnResponse=dlg.DoModal();if(nResponse==1DOK)(//TODO:Placecodeheretohandlewhenthedialogis//dismissedwithOKelseif(nResponse==IDCANCEL)//TODO:Placecodeheretohandlewhenthedialogis//dismissedwithCancel))catch(…){〃dump出系統(tǒng)的ー些重要信息,并通知管理員查找出現(xiàn)意外異常的原因。/Z同時想辦法恢復(fù)系統(tǒng),例如說重新啟動應(yīng)用程序等)//Sincethedialoghasbeenclosed,returnFALSEsothatweexitthe//application,ratherthanstarttheapplication'smessagepump.returnFALSE;)通過上面的例程和分析可以得出,由于catch(…)能夠捕獲所有數(shù)據(jù)類型的異常對象,所以在恰當(dāng)?shù)牡胤绞褂胏atch(…)確實(shí)可以使軟件系統(tǒng)有著更好的可靠性。這確實(shí)是大家使用catch(…)這個東東最好的理由。但不要誤會的是,在C++異常處理模型中,不只有catch(…)方法能夠捕獲幾乎所有類型的異常對象(也許有其它更好的方法,在下?篇文章中主人公阿愚帶大家一同去探討一下),可C++標(biāo)準(zhǔn)中為什么會想到定義這樣ー個catch(…)呢?有過java或C#編程開發(fā)經(jīng)驗(yàn)的程序員會發(fā)現(xiàn),在它們的異常處理模型中,并沒有這樣類似的ー種語法,可這里不得不再次強(qiáng)調(diào)的是,java中的異常處理模型是C++中的異常處理模型的完善改進(jìn)版,可它反而沒有了catch(-),為何呢?還是先去看看下一章吧,“C++的異常處理和面向?qū)ο蟮木o密關(guān)系”。也許大家能找到ー個似乎合理的原因。第4集C++的異常處理和面向?qū)ο蟮木o密
關(guān)系如果有人問起C++和C到底有那些本質(zhì)上的不同點(diǎn)?主人公阿愚當(dāng)然也會有自己的一份理解,他會毫不猶豫回答出:“與C相比,C++至少引入了兩項(xiàng)重要技術(shù),其一就是對面向?qū)ο蟮娜嬷С?還有一項(xiàng)就是C++優(yōu)良的異常處理模型”。是的,這兩項(xiàng)技術(shù)對構(gòu)建出ー個優(yōu)良的可靠復(fù)雜的軟件系統(tǒng)都太重要了??蛇@兩項(xiàng)技術(shù)之間又有何關(guān)系呢?非??陀^公正的說,它們之間的關(guān)系實(shí)在是太緊密了,兩者相互支持和依賴,是構(gòu)建優(yōu)良可靠復(fù)雜的軟件系統(tǒng)最不可缺乏的兩個東東。用對象來描述程序中出現(xiàn)的異常雖然前幾篇文章的內(nèi)容中列舉的ー些小例子程序大多都是throwー些如int、double類型的異常,但程序員朋友都很熟悉,實(shí)際開發(fā)環(huán)境中所拋出的異常都是一個個代表抽象數(shù)據(jù)類型的對象,如C++標(biāo)準(zhǔn)庫中的std::exception(),MFC開發(fā)庫中CException等。用對象來描述的我們程序中的出現(xiàn)異常的類型和異常信息是C++異常處理模型中最閃光之處,而且這ー特點(diǎn)一直沿用到j(luò)ava語言的異常處理模型中。為什么要用對象來描述程序中出現(xiàn)的異常呢?這樣做的優(yōu)勢何在?主人公阿愚不喜歡窮擺出什么大道理,還是老辦法,從具體的實(shí)例入手。由于異常有許許多多種類型,如有致命的錯誤、一般的錯誤、警告或其它事件通知等,而且不同類型的異常有不同的處理方法,有的異常是不可恢復(fù)的,而有的異常是可以恢復(fù)的(專業(yè)術(shù)語叫做‘‘重入”吧!哈哈,主人公阿愚有時也會來點(diǎn)文經(jīng)紿的東西),所以程序員在開發(fā)系統(tǒng)時就必須考慮把各種可能出現(xiàn)的異常進(jìn)行分類,以便能夠分別處理。下面為ー個應(yīng)用系統(tǒng)設(shè)計(jì)出ー個對異常進(jìn)行分類的簡單例子,如下:
從上面的異常分類來看,它有明顯的層次性和繼承性,這恰恰和面向?qū)ο蟮睦^承思想如出一轍,因此用對象來描述程序中出現(xiàn)的異常是再恰當(dāng)不過的了。而且可以利用面向?qū)ο蟮奶匦院芎玫膶Ξ惓_M(jìn)行分類處理,例如有這樣ー個例子:voidOpenFile(stringf)(try(/Z打開文件的操作,可能拋出FileOpenException)catch(FileOpenException&fe){/Z處理這個異常,如果這個異??梢院芎玫牡靡曰謴?fù),那么處理完畢后函數(shù)//正常返回;否則必須重新拋出這個異常,以供上層的調(diào)用函數(shù)來能再次處/Z理這個異常對象intresult=ReOpenFile(f);if(result==false)throw;voidReadFile(Filef)try(/Z從文件中讀數(shù)據(jù),可能拋出FileReadException)catch(FileReadException&fe){/Z處理這個異常,如果這個異??梢院芎玫牡靡曰謴?fù),那么處理完畢后函數(shù)//正常返回;否則必須重新拋出這個異常,以供上層的調(diào)用函數(shù)來能再次處/Z理這個異常對象intresult=ReReadFile(f);if(result==false)throw;}}voidWriteFile(Filef)(try{//往文件中寫數(shù)據(jù),可能拋出FileWriteException)catch(FileWriteException&fe)(/Z處理這個異常,如果這個異??梢院芎玫牡靡曰謴?fù),那么處理完畢后函數(shù)//正常返回;否則必須重新拋出這個異常,以供上層的調(diào)用函數(shù)來能再次處/Z理這個異常對象intresult=ReWriteFile(f);if(result==false)throw;))voidFunc(){try(〃對文件進(jìn)行操作,可能出現(xiàn)FileWriteException、FileWriteException/Z和FileWriteException異常OpenFile(…);ReadFile(…);WriteFile(…);)//注意:FileException是FileOpenException、FileReadException和FileWriteException/Z的基類,因此這里定義的catch(FileException&fe)能捕獲所有與文件操作失敗的異/Z常。catch(FileException&fe)Exceptioninfo*ef=fe.GetExceptionlnfo();cout?"操作文件時出現(xiàn)了不可恢復(fù)的錯誤,原因是:"<<fe?endl;通過上面簡單的例子可以看出,利用面向?qū)ο蟮姆椒?確實(shí)能很好地對異常進(jìn)行分類處理,分層處理,如果異常能得以恢復(fù)的盡可能去實(shí)現(xiàn)恢復(fù),否則向上層重新拋出異常表明當(dāng)前的函數(shù)不能對這里異常進(jìn)行有效恢復(fù)。同時特別值得?提的是,上層的catchblock利用申明一個基類的異常對象作為catch關(guān)鍵字的參數(shù),使得提供了對多種類型的異常對象的集中處理方法,這就是上一篇文章中所提到的除了catch(…)以外,還有其它的來實(shí)現(xiàn)對多種類型的異常對象的集中處理方法,而且利用對象基類的方法顯然要比catch(…)優(yōu)雅很多,方便很多,要知道在catch(…)的異常處理模塊中是沒有多少辦法獲取ー些關(guān)于異常出現(xiàn)時異常具體信息的,而對象基類的方法則完全不同,異常處理模塊可以訪問到真正的異常對象。現(xiàn)在回想一下上一篇文章中提出的那個問題?就是既然有其它很好的方法(利用類的繼承性)來可以代替catch(…)提供的異常集中處理,那為什么C++標(biāo)準(zhǔn)中還偏要提供catch(-)這樣ー種奇怪的語法呢?其實(shí)這還是由于C++本身ー些特點(diǎn)所決定的,因?yàn)榇蠹叶贾?,C++在業(yè)界有很多的版本,更重要的是沒有一個統(tǒng)ー的標(biāo)準(zhǔn)開發(fā)類庫,或者說沒有統(tǒng)一的標(biāo)準(zhǔn)開發(fā)環(huán)境,雖然存在標(biāo)準(zhǔn)C庫和標(biāo)準(zhǔn)C++庫,但這遠(yuǎn)遠(yuǎn)不夠,構(gòu)成不了一個完整的開發(fā)支撐環(huán)境,因此在許多重要的開發(fā)庫中都各自為政,它們在自己的開發(fā)庫都各自定義了一套對異常進(jìn)行分類支持的庫。因此應(yīng)用程序的開發(fā)環(huán)境往往都同時需要依賴于幾個基礎(chǔ)開發(fā)庫之上(例如MFC+XML4C+StandardC++),這樣對開發(fā)人員而言,便沒有一個共同的異常對象的基類,所以C++標(biāo)準(zhǔn)中便提供了catch(…)來捕獲所有異常,這確實(shí)是ー種不得已而為之的折衷方法(哈哈!這個理解完全是主人公阿愚自己一相情愿,ー個人胡思亂想而出來的,朋友們?nèi)缬胁煌囊庖娍梢院桶⒂抟黄鹩懻?),另外JAVA中則不會出現(xiàn)這種情況,因?yàn)镴DK是統(tǒng)ー的,所有的異常對象都是從java.lang.Throwable接口繼承而來的,因此只要在程序的入口函數(shù)中catch(java.lang.Throwableall),便可以捕獲所有的異常。所以在JAVA的異常處理模型中沒有類似C++那樣ー個catch(…)的東東,完全沒必要。異常處理中采用面向?qū)ο蠹夹g(shù)還有哪些好處呢?上面講到,用對象來描述程序中出現(xiàn)的異常除了能很好地分層處理異常外,還有那些好處呢?當(dāng)然除了好處大大的外,好處也是多多的,例如:(1)面向?qū)ο蟮膶?shí)現(xiàn)中,一般都很好的實(shí)現(xiàn)了對象的RTTI技術(shù),如果異常用對象來表示,那么就可以很好完成異常對象的數(shù)據(jù)類型匹配,還有就是函數(shù)的多態(tài),例如上面的那個例子中,即便是到了catch(FileException&fe)異常處理模塊中,也能知道到底是出現(xiàn)了那種具體的異常,是FileOpenException呢?還是其它的異常?(2)面向?qū)ο蟮膶?shí)現(xiàn)中,一般都很好的實(shí)現(xiàn)了對象的構(gòu)造、對象的銷毀、對象的轉(zhuǎn)存復(fù)制等等,所以這也為異常處理模型中,異常對象的轉(zhuǎn)存復(fù)制和對象銷毀提供了很好的支持,容易控制;(3)其它的嗎?暫時沒有,可能還有沒想到的。在異常處理的分層管理下,異常對象的重新拋出往往非常常見,本文中剛オ的例子就有這樣的情況,但當(dāng)時僅ー筆帶過而已,為了表明對它的重視,下一篇文章重點(diǎn)討論ー下異常對象的rethrow處理。第5集C++的異常rethrow上一篇文章已經(jīng)提到了C++的異常rethrow現(xiàn)象,這篇文章將進(jìn)ー步深入討論這個問題。當(dāng)catch到ー個異常后進(jìn)入異常處理程序塊中,此時需要從傳入的異常對象中得到ー些關(guān)于異常的詳細(xì)信息,并判斷這個異常是否能得以恢復(fù),是否能在當(dāng)前的異常處理程序塊中得以處理。如果是,那么及時地處理掉這個異常,使程序恢復(fù)到正常的工作軌道上(也即從catchblock后面的代碼處繼續(xù)執(zhí)行):否則就必須重新拋出異常(ExcptionRethrow),把這個異常交給上?層函數(shù)的異常處理模塊去處理(反正我是處理不了了,而且我也通知了我的上層領(lǐng)導(dǎo),所以責(zé)任嗎,也就不由我擔(dān)當(dāng)了,哈哈ハハ)。語法很簡單,有兩種用法,如下:throw;throwexception_obj;第一種表示原來的異常對象再次被重新拋出:第二中呢,則表示原來的異常已處理或正在處理中,但此時又引發(fā)了另ー個異常。示例如下:voidmain(){try(try{throw4;}catch(intvalue){/Z第一種用法,原來的異常被再次拋出//注意它不需要帶參數(shù)。throw;)try(throw0.5;)catch(doublevalue){〃第二種用法,再次拋出另外的ー個異常/Z它的語法和其它正常拋出異常的語法一樣。throw**anotherexceptionM;catch(...)cout?”unknowexception"?endl;))在什么地方異常可以rethrow?當(dāng)然,異常的rethrow只能在catchblock中,或者說在catchblock中拋出的異常才是異常的rethrow,因此注意下面的示例程序中存在語法錯誤,如下:voidmain()(try(try(throw4;)catch(intvalue)(/Z這里的語法是對的。throw;}/Z但這里的語法卻是不對的。//不能在這里進(jìn)行異常的rethrowthrow;)catch(...)(cout?uunknowexceptionM?endl;異常!vthmw需要注意的問題!異常rethrow需要注意什么問題呢?看例子先!voidmain()(try(try(throw4;catch(intvalue)(/Z異常的rethrowthrow;)catch(...)(cout?”能打印我這條消息嗎?”?endl;))catch(...)(cout?**unknowexceptionw?endl;上面的程序運(yùn)行結(jié)果是:"unknowexception,,由此我們可以得出結(jié)論,異常的rethrow后,它會在它上一層的try/catch塊開始查找匹配的catchblock異常處理塊,而在同一層中,如果當(dāng)前的catchblock后面還有其它的catchblock,它是不會去匹配的。所以程序中一般層次模型的try/catch要比線性結(jié)構(gòu)的try/catch要好一些,如下(示例2要比示例1好):/Z示例1voidmain(){try()catch(DataTypel&){)catch(DataType2&)()catch(DataType3&){}catch(...)/Z示例2voidmain()trytrytry(try()catch(DataTypel&)catch(DataType2&)catch(DataType3&)catch(...)總結(jié)相遇篇的文章到此結(jié)束。通過這幾篇文章的介紹,目前已經(jīng)對異常處理編程的思想,C++異常處理模型、語法,以及C++異常處理與面向?qū)ο蟮年P(guān)系等等,都有了一個大概性的了解。主人公阿愚根據(jù)自己的理解和經(jīng)驗(yàn),現(xiàn)在對相遇篇中的知識再做出如下ー些總結(jié):(1)異常處理編程和面向?qū)ο螅乾F(xiàn)在程序設(shè)計(jì)和編程中最不可缺少的兩個好東東:C++異常處理模型是層次型的,能很好地支持嵌套;C++異常處理編程提供try、catch和throw三個關(guān)鍵字。其中try定義受監(jiān)控的程序塊;catch定義異常處理模塊;throw讓程序員可以在程序執(zhí)行出錯的地方拋出異常;C++異常處理模型的實(shí)現(xiàn)充分使用到了面向?qū)ο蟮乃枷牒头椒?C++異常處理模型中,異常是可以ethrow的。從下篇文章開始,主人公阿愚對異常處理編程將進(jìn)入到了一個相知的階段。這ー階段中,阿愚將全面性地去深入了解異常處理編程中的各個細(xì)節(jié)和一些特點(diǎn),并根據(jù)自己的理解闡述ー些異常處理編程設(shè)計(jì)思想方面的東西。各位程序員朋友們,準(zhǔn)備好了嗎?Let'sgo!第6集對象的成員函數(shù)中拋出的異常C++異常處理模型除了支持面向過程的C風(fēng)格程序中的異常處理外(就是沒有面向?qū)ο蟮母拍?完全是C程序,整個程序?qū)嶋H就是函數(shù)的集合,但卻用C++編譯器來編譯這樣的C程序,所以這樣的程序中是可以a使用C++的異常處理機(jī)制的,要不怎么說C++是兼容C語言的呢?但是需要注意的是,單純的C語言程序中是不能使用C++異常處理模型進(jìn)行編程的。是不是有點(diǎn)說拗口了?有點(diǎn)糊涂了呢?其實(shí)很簡單,那就是如果程序中使用了C++異常處理機(jī)制,也即代碼中有try、catch和throw關(guān)鍵字,那么就必須使用C++編譯器來編譯這個程序。許多程序員朋友們在這里有一個理解上的誤區(qū),認(rèn)為只有程序中使用了面向?qū)ο蟮母拍?,即使用class關(guān)鍵字來定義ー個類結(jié)構(gòu)オ算得上C++程序,其實(shí)這種理解是片面的,如果程序中采用了C++異常處理機(jī)制,那么也有理由認(rèn)為這是ー個C++程序,哪怕程序的代碼完全是C語言風(fēng)格的,并且這樣的程序用C編譯器來編譯肯定將會報(bào)錯,提示未定義的try標(biāo)示符等等錯誤信息),還支持面向?qū)ο蟪绦蛑袑ο髵伋龅漠惓L幚?。C++異常處理模型的確和面向?qū)ο笫蔷o密結(jié)合的,除了在相遇篇中介紹到的用對象來描述程序中出現(xiàn)的異常之外,C++異常處理模型也對在面向?qū)ο蟪绦蛑械膶ο髮?shí)例所拋出的異常作了最完善的支持和處理。也許大家會覺得這很容易,沒什么了不起的地方。但恰恰相反,實(shí)際上這オ是C++異常處理模型最成功、最不可思議和最閃光的地方。而且由于C++異常處理模型對面向?qū)ο笥辛撕芎玫闹С趾图嫒?才使得C++異常處理模型本身的實(shí)現(xiàn)變得特別復(fù)雜,因?yàn)樗枰櫭咯`個對象的運(yùn)行情況和狀態(tài)(關(guān)于C++異常處理模型的實(shí)現(xiàn),會在愛的秘密篇中有詳細(xì)剖析)。本文和接下來的幾篇文章將講述當(dāng)對象實(shí)例拋出異常時將如何處理。對象的生命周期一般有三種狀態(tài):構(gòu)造、運(yùn)行和析構(gòu)銷毀。因此對象拋出的異常也有這三種區(qū)別。是在對象構(gòu)造時拋出的呢?還是對象運(yùn)行時拋出的呢?或是析構(gòu)對象時拋出的?這三種不同時候拋出的異常會將會產(chǎn)生不同的結(jié)果。本文首先討論最常見的一種情況,在對象運(yùn)行時拋出的異常,也即執(zhí)行對象的成員函數(shù)時出現(xiàn)的異常。對象的成員函數(shù)拋出的異常1、老方法,看例子先,如下:classMyTest_Base(public:MyTest_Base(stringname=""):m_name(name)(cout?"構(gòu)造—"個MyTest_Base類型的對象,對象名為:"<<m_name?endl;}virtua!?MyTest_Base()cout?"銷毀ー個MyTest-Base類型的對象,對象名為:"《m_name?endl;voidFunc()throw()(throwstd::exception(“故意拋出ー個異常,測試!”);)voidOther(){}protected:stringm_name;};voidmain()(try(MyTest_Baseobjl(<4objlw);/Z調(diào)用這個成員函數(shù)將拋出ー個異常,注意objl的析構(gòu)函數(shù)會被執(zhí)行嗎?如果//會,又是在什么時候被執(zhí)行呢?objl.Func();objl.Other();)catch(std::exceptione)(cout?e.what()?endl;}catch(...)(cout?”unknowexception"?endl;))C++程序員不難看出上面的程序的運(yùn)行結(jié)果,如下:構(gòu)造ー個MyTest_Base類型的對象,對象名為:objl銷毀ー個MyTest_Base類型的對象,對象名為:objl故意拋出ー個異常,測試!從運(yùn)行結(jié)果可以得出如下結(jié)論:(1)對象的成員函數(shù)出現(xiàn)異常時,catchblock能捕獲到異常,這一點(diǎn)就像C語言中的普通函數(shù)一樣,沒什么特別的地方:(2)對象的成員函數(shù)出現(xiàn)異常時,對象的析構(gòu)函數(shù)將會得到執(zhí)行(這一點(diǎn)很神奇吧!當(dāng)然在這里不會做過多研究,在剖析C++異常處理模型的實(shí)現(xiàn)時再做詳細(xì)的闡述),這里與C++標(biāo)準(zhǔn)中規(guī)定的面向?qū)ο蟮奶匦允窍嘁恢碌?,?gòu)造了的對象就必須保證在適當(dāng)?shù)牡胤揭鰳?gòu)它,以釋放可能的資源。因此前面說的“C++異常處理模型對面向?qū)ο筇峁┝酥С趾图嫒?‘是有根據(jù)的。而且注意它的析構(gòu)函數(shù)是在異常處理模塊之前執(zhí)行的,這一點(diǎn)更與C++標(biāo)準(zhǔn)中規(guī)定的面向?qū)ο蟮奶匦允且恢碌模?dāng)對象出了作用域時,它就必須要被析構(gòu)。2、把上面的程序小改?下,運(yùn)行再看結(jié)果,如下:voidmain()//obj!對象不在try/catch域中,注意它的析構(gòu)函數(shù)在什么時候被執(zhí)行?MyTest_Baseobjl(“objド);try(//obj2和obj3對象都在try/catch域中,其中obj3.Func()函數(shù)被調(diào)用,因此〃〇bj3會拋出異常,特別需要注意的是,obj2的析構(gòu)函數(shù)會被執(zhí)行嗎?如果//會,又是在什么時候被執(zhí)行呢?MyTest_Baseobj2(“obj2”),obj3(“obj3”);obj3.Other();/Z調(diào)用這個成員函數(shù)將拋出ー個異常obj3.Func();〃注意:。切4對象在構(gòu)造之前,函數(shù)中就有異常拋出。所以。bj4對象將不會/Z被構(gòu)造,當(dāng)然也不會被析構(gòu)MyTest_Baseobj4(**obj4w);obj3.0ther();)catch(std::exceptione)(cout?e.what()?endl;}catch(...){cout?”unknowexception"?endl;上面的程序也難看出其運(yùn)行結(jié)果,如下:構(gòu)造ー個MyTest_Base類型的對象,對象名為:objl構(gòu)造ー個MyTest_Base類型的對象,對象名為:obj2構(gòu)造ー個MyTest.Base類型的對象,對象名為:obj3銷毀ー個MyTest一Base類型的對象,對象名為:obj3銷毀ー個MyTest-Base類型的對象,對象名為:obj2故意拋出ー個異常,測試!銷毀ー個MyTest_Base類型的對象,對象名為:objl結(jié)合程序中提出的問題和運(yùn)行結(jié)果,可以又可得出如下結(jié)論:(1)在成員函數(shù)出現(xiàn)異常時,同一個作用域中異常出現(xiàn)點(diǎn)后面還未來得及構(gòu)造的對象將不會被構(gòu)造,當(dāng)然也不會被析構(gòu);(2)在成員函數(shù)出現(xiàn)異常時,同一個作用域中異常出現(xiàn)點(diǎn)前面已經(jīng)構(gòu)造的對象也同樣會被析構(gòu)(這是不是更神奇了!)。因此這也顯現(xiàn)出C++異常處理不會破壞C++標(biāo)準(zhǔn)中規(guī)定的面向?qū)ο蟮奶匦?,?dāng)對象出了作用域時,它就必須要被析構(gòu),即便它自己本身沒出現(xiàn)異常,總之不管是正常的執(zhí)行過程導(dǎo)致對象退出了作用域,還是其它對象運(yùn)行時發(fā)生了異常而導(dǎo)致自己退出了作用域;(3)在成員函數(shù)出現(xiàn)異常時,未被影響到的其它作用域中的對象將保持自己原來的執(zhí)行流程。對象的成員函數(shù)拋出的異常時概括性總結(jié)哈哈ハ-A,其是就只有一句話,那就是“C++的異常處理不會破壞任何一條面向?qū)ο蟮奶匦?”,因此主人公阿愚建議大家其實(shí)無須要記住上面總結(jié)的n條結(jié)論,記住這一條足矣!下篇文章討論在構(gòu)造函數(shù)中拋出異常時程序的執(zhí)行情況,這有點(diǎn)復(fù)雜呀!朋友們,Let'sgo!第7集構(gòu)造函數(shù)中拋出的異常上一篇文章簡單討論了一下對象的成員函數(shù)拋出異常時的處理情況。本文中將繼續(xù)討論當(dāng)在構(gòu)造函數(shù)中拋出異常時,程序的執(zhí)行情況又如何?這有點(diǎn)復(fù)雜呀!而且主人公阿愚還覺得這蠻有點(diǎn)意思!構(gòu)造函數(shù)中拋出的異常1、標(biāo)準(zhǔn)C++中定義構(gòu)造函數(shù)是ー個對象構(gòu)建自己,分配所需資源的地方,一旦構(gòu)造函數(shù)執(zhí)行完畢,則表明這個對象已經(jīng)誕生了,有自己的行為和內(nèi)部的運(yùn)行狀態(tài),之后還有對象的消亡過程(析構(gòu)函數(shù)的執(zhí)行)。可誰能保證對象的構(gòu)造過程一定能成功呢?說不定系統(tǒng)當(dāng)前的某個資源不夠,導(dǎo)致對象不能完全構(gòu)建好自己(人都有畸形兒,更何況別的呢?朋友們!是吧!),因此通過什么方法來表明對象的構(gòu)造失敗了呢?C++程序員朋友們知道,C++中的構(gòu)造函數(shù)是沒有返回值的,所以不少關(guān)于C++編程方面的書上得出結(jié)論:“因?yàn)闃?gòu)造函數(shù)沒有返回值,所以通知對象的構(gòu)造失敗的唯一方法那就是在構(gòu)造函數(shù)中拋出異?!?。主人公阿愚非常不同意這種說法,誰說的,便不信邪!雖然C++標(biāo)準(zhǔn)規(guī)定構(gòu)造函數(shù)是沒有返回值,可我們知道每個函數(shù)實(shí)際上都會有一個返回值的,這個值被保存在eax寄存器中,因此實(shí)際上是有辦法通過編程來實(shí)現(xiàn)構(gòu)造函數(shù)返回一個值給上層的對象創(chuàng)建者。當(dāng)然即便是構(gòu)造函數(shù)真的不能有返回值,我們也可以通過ー個指針類型或引用類型的出參來獲知對象的構(gòu)造過程的狀態(tài)。示例如下:classMyTest_Base(public:MyTest_Base(int&status)(//dootherjob//由于資源不夠,對象構(gòu)建失敗〃把status置0,通知對象的構(gòu)建者status=0;)protected:);voidmain()(intstatus;MyTest_Baseobjl(status);/Z檢查對象的構(gòu)建是否成功if(status==0)cout?”對象構(gòu)建失敗”《endl;)程序運(yùn)行的結(jié)果是:對象構(gòu)建失敗是啊!上面我們不也得到了對象構(gòu)造的成功與否的信息了嗎?可大家有沒有覺得這當(dāng)中有點(diǎn)問題?主人公阿愚建議大家在此停留片刻,仔細(xì)想想它會有什么問題?0K!也許大家都知道了問題的所在,來驗(yàn)證一下吧!classMyTest_Base(public:MyTest_Base(int&status)(//dootherjob/Z由于資源不夠,對象構(gòu)建失敗//把status置0,通知對象的構(gòu)建者status=0;)virtual?MyTest_Base()(cout?”銷毀ー個MyTest_Base類型的對象”?endl;}protected:);voidmain(){intstatus;MyTest_Baseobjl(status);/Z檢查對象的構(gòu)建是否成功if(status==0)cout?”對象構(gòu)建失敗"?endl;)程序運(yùn)行的結(jié)果是:對象構(gòu)建失敗銷毀ー個MyTest_Base類型的對象沒錯,對象的析構(gòu)函數(shù)被運(yùn)行了,這與C++標(biāo)準(zhǔn)中所規(guī)定的面向?qū)ο蟮末`些特性是有沖突的。ー個對象都沒有完成自己的構(gòu)造,又何來析構(gòu)!好比ー個夭折的畸形兒還沒有出生,又何來死之言。因此這種方法是行不通的。那怎么辦?那就是上面那個結(jié)論中的后一句話是對的,通知對象的構(gòu)造失敗的唯一方法那就是在構(gòu)造函數(shù)中拋出異常,但原因卻不是由于構(gòu)造函數(shù)沒有返回值而造成的。恰恰相反,C++標(biāo)準(zhǔn)中規(guī)定構(gòu)造函數(shù)沒有返回值正是由于擔(dān)心很容易與面向?qū)ο蟮末`些特性相沖突,因此干脆來個規(guī)定,構(gòu)造函數(shù)不能有返回值(主人公阿愚的個人理解,有不同意見的朋友歡迎討論)。2、構(gòu)造函數(shù)中拋出異常將導(dǎo)致對象的析構(gòu)函數(shù)不被執(zhí)行。哈哈ヘヘ阿愚很開心,瞧瞧!如果沒有C++的異常處理機(jī)制鼎立支持,C++中的面向?qū)ο筇匦远紵o法真正實(shí)現(xiàn)起來,C++標(biāo)準(zhǔn)總不能規(guī)定所有的對象都必須成功構(gòu)造吧!這也太理想化了,也許只有等到共產(chǎn)主義社會實(shí)現(xiàn)的那一天(CPU可以隨便拿,內(nèi)存可以隨便拿,所有的資源都是你的!)オ說不定有麗自????,所以說C++的異常處理和面向?qū)ο蟠_實(shí)是誰也離不開誰。當(dāng)然示例還是要看?下,如下:classMyTest_Base(public:MyTest_Base(stringname=""):m_name(name)(throwstd::exception("在構(gòu)造函數(shù)中拋出ー個異常,測試!”);cout?”構(gòu)造ー個MyTest_Base類型的對象,對象名為:"?m_name?endl;)virtual-MyTest_Base()(cout?“銷毀ー個MyTest_Base類型的對象,對象名為:"vvm_name<<endl;}voidFunc()throw()(throwstd::exception(“故意拋出ー個異常,測試!”);)voidOther(){}protected:stringm_name;voidmain()(try{/Z對象構(gòu)造時將會拋出異常MyTest_Baseobjl(**obj1n);objl.Func();objl.Other();}catch(std::exceplione)(cout?e.what()?endl;)catch(...){cout?**unknowexceptionM?endl;})程序的運(yùn)行結(jié)果將會驗(yàn)證:“構(gòu)造函數(shù)中拋出異常將導(dǎo)致對象的析構(gòu)函數(shù)不被執(zhí)行”3、是不是到此,關(guān)于構(gòu)造函數(shù)中拋出異常的處理的有關(guān)討論就能結(jié)束了呢?非也!非也!主人公阿愚還有進(jìn)ー步的故事需要講述!來看ー個更復(fù)雜ー點(diǎn)的例子吧!如下:classMyTest_Basepublic:MyTest_Base(stringname=**'*):m_name(name)(cout?”構(gòu)造ー個MyTest_Base類型的對象,對象名為:"<vm_name?endl;)virtual?MyTest_Base()(coutvv”銷毀ー個MyTest_Base類型的對象,對象名為:“vvm_name?endl;)voidFunc()throw()(throwstd:exception("故意拋出ー個異常,測試!M);}voidOther(){}protected:stringm_name;};classMyTest_Parts(public:MyTest_Parts()(cout<v”構(gòu)造ー個MyTest_Parts類型的對象"?endl;}virtual?MyTest_Parts()(cout?"銷毀ー個MyTest_Parts類型的對象"<<endl;));classMyTest_Derive:publicMyTest_Base{public:MyTest_Derive(stringname=""):m_component(),MyTest_Base(name)(throwstd:exception("在MyTest_Derive對象的構(gòu)造函數(shù)中拋出了一個異常!");cout?"構(gòu)造ー個MyTest_Derive類型的對象,對象名為:"<<m_name?endl;}virtual~MyTest_Derive()(cout?"銷毀ー個MyTest_Derive類型的對象,對象名為:"<<m_name?endl;)protected:MyTest_Partsm_component;);voidmain()try(/Z對象構(gòu)造時將會拋出異常MyTest_Deriveobjl("obj1");objl.Func();objl.Other();)catch(std::exceptione)(cout?e.what()?endl;}catch(...)(cout?"unknowexception"?endl;程序運(yùn)行的結(jié)果是:構(gòu)造ー個MyTest_Base類型的對象,對象名為:objl構(gòu)造ー個MyTest_Parts類型的對象銷毀ー個MyTest_Parts類型的對象銷毀ー個MyTest_Base類型的對象,對象名為:objl在MyTest_Derive對象的構(gòu)造函數(shù)中拋出了一個異常!上面這個例子中,MyTest_Derive從MyTest_Base繼承,同時MyTest_Derive還有一個MyTest_Parts類型的成員變量。現(xiàn)在MyTest_Derive構(gòu)造的時候,是在父類MyTest_Base已構(gòu)造完畢和MyTest_Parts類型的成員變量m_component也已構(gòu)造完畢之后,再拋出了一個異常,這種情況稱為對象的部分構(gòu)造。是的,這種情況很常見,對象總是由不斷的繼承或不斷的聚合而來,對象的構(gòu)造過程實(shí)際上是這些所有的子對象按規(guī)定順序的構(gòu)造過程,其中這些過程中的任何ー個子對象在構(gòu)造時發(fā)生異常,對象都不能說自己完成了全部的構(gòu)造過程,因此這里就有一個棘手的問題,當(dāng)發(fā)生對象的部分構(gòu)造時,對象將析構(gòu)嗎?如果時,又將如何析構(gòu)呢?從運(yùn)行結(jié)果可以得出如下結(jié)論:(1)對象的部分構(gòu)造是很常見的,異常的發(fā)生點(diǎn)也完全是隨機(jī)的,程序員要謹(jǐn)慎處理這種情況:(2)當(dāng)對象發(fā)生部分構(gòu)造時,已經(jīng)構(gòu)造完畢的子對象將會逆序地被析構(gòu)(即異常發(fā)生點(diǎn)前面的對象);而還沒有開始構(gòu)建的子對象將不會被構(gòu)造了(即異常發(fā)生點(diǎn)后面的對象),當(dāng)然它也就沒有析構(gòu)過程了;還有正在構(gòu)建的子對象和對象自己本身將停止繼續(xù)構(gòu)建(即出現(xiàn)異常的對象),并且它的析構(gòu)是不會被執(zhí)行的。構(gòu)造函數(shù)中拋出異常時概括性總結(jié)C++中通知對象構(gòu)造失敗的唯一方法那就是在構(gòu)造函數(shù)中拋出異常;(2)構(gòu)造函數(shù)中拋出異常將導(dǎo)致對象的析構(gòu)函數(shù)不被執(zhí)行;(3)當(dāng)對象發(fā)生部分構(gòu)造時,已經(jīng)構(gòu)造完畢的子對象將會逆序地被析構(gòu);(4)哈哈ハ一へ其是還是那句話,“C++的異常處理不會破壞任何一條面向?qū)ο蟮奶匦?”,因此主人公阿愚再次建議朋友們,牢牢記住這一條!下ー篇文章討論在對象的析構(gòu)函數(shù)中拋出異常時程序的執(zhí)行情況,這不僅有些復(fù)雜,而且很關(guān)鍵,它對我們的軟件系統(tǒng)影響簡直太大了,可許多人并未意識到這個問題的嚴(yán)重性!第8集析構(gòu)函數(shù)中拋出的異常前兩篇文章討論了對象在構(gòu)造過程中(構(gòu)造函數(shù))和運(yùn)行過程中(成員函數(shù))出現(xiàn)異常時的處理情況,本文將討論最后一種情況,當(dāng)異常發(fā)生在對象的析構(gòu)銷毀過程中時,又會有什么不同呢?主人公阿愚在此可以非常有把握地告訴大家,這將會有大大的不同,而且處理不善還將會毫不留情地影響到軟件系統(tǒng)的可靠性和穩(wěn)定性,后果非常嚴(yán)重。不危言聳聽了,看正文吧!析構(gòu)函數(shù)在什么時候被調(diào)用執(zhí)行?對于C++程序員來說,這個問題比較簡單,但是比較愛嘮叨的阿愚還是建議應(yīng)該在此再提ー提,也算回顧一下C++的知識,而且這將對后面的討論和理解由一定幫助。先看ー個簡單的示例吧!如下:classMyTest_Base(public:virtual?MyTest_Base()(cout?"銷毀ー個MyTest_Base類型的對象"vvendl;}};voidmain()(try(/Z構(gòu)造ー個對象,當(dāng)obj對象離開這個作用域時析構(gòu)將會被執(zhí)行MyTest_Baseobj;)catch(...)(cout?"unknowexception"?endl;編譯運(yùn)行上面的程序,從程序的運(yùn)行結(jié)果將會表明對象的析構(gòu)函數(shù)被執(zhí)行了,但什么時候被執(zhí)行的呢?按C++標(biāo)準(zhǔn)中規(guī)定,對象應(yīng)該在離開它的作用域時被調(diào)用運(yùn)行。實(shí)際上各個廠商的C++編譯器也都滿足這個要求,拿VC來做個測試驗(yàn)證吧!,下面列出的是剛剛上面的那個小示例程序在調(diào)試時拷貝出的相關(guān)程序片段。注意其中obj對象將會在離開tryblock時被編譯器插入一段代碼,隱式地來調(diào)用對象的析構(gòu)函數(shù)。如下:325:try326:(00401311movdwordptr[ebp-41,0327:/Z構(gòu)造ー個對象.當(dāng)??蓪ο箅x開這個作用域時析構(gòu)將會被執(zhí)行MyTest_Baseobj;00401318leaecxjobj]0040131Beall@ILT440(MyTest_Base::M>Test_Base)(0040102d)J/Z瞧ド面,編譯器插入一段代碼,隱式地來調(diào)用對象的析構(gòu)函數(shù)00401320leaecxjobj]00401323call@ILT+15(Mylest_Base:>MyTest_Base)(00401014)catch(...)00401328jmp_lynd$_main$I(00401365){cout?"unknowexception"?endl;0040132Amovesi,esp0040132Cmoveax,[_imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@I@AAV21@@Z(0041610c)00401331pusheax00401332movedi.esp00401334pushoffsetstring"unknowexception"(0041401c)00401339movecx,dwordptr[_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@l@A(00416124)0040133Fpushecx00401340calldwordptr[_imp_??6std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAVI0@PBD@Z(0()400401346addesp.800401349empedi.esp0040134Bcall_chkesp(OO4OI6b2)00401350movecx,eax00401352calldwordptr[_imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAjW01@P6AAiW01@AAV0100401358empesi,esp0040135Acall_chkesp(004016b2)}0040135Fmoveax,offset_tryend$_main$1(00401365)00401364ret}析構(gòu)函數(shù)中拋出的異常1、仍然是先看示例,如下:classMyTest_Base(public:virtual?MyTest_Base()cout?”開始準(zhǔn)備銷毀ー個MyTest_Base類型的對象”vvendl;〃注意:在析構(gòu)函數(shù)中拋出了異常throwstd::exception("在析構(gòu)函數(shù)中故意拋出ー個異常,測試!ッ;voidFunc()throw()(throwstd::exception("故意拋出ー個異常,測試!ツ;)voidOther(){});voidmain()(try(/Z構(gòu)造ー個對象,當(dāng)obj對象離開這個作用域時析構(gòu)將會被執(zhí)行MyTest_Baseobj;obj.Other();)catch(std::exceptione)(cout?
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 分期購車銀行合同范本
- 兼職廚師勞務(wù)合同范本
- 代理建賬合同范本
- 入職各種合同范本
- 2025年湖南a2貨運(yùn)從業(yè)資格證考試
- 介紹客戶返利合同范本
- 農(nóng)田托管合同范本
- 公司對外工程合同范本
- 公司代理注冊合同范本
- 凍庫銷售合同范本
- 《綠色建筑設(shè)計(jì)原理》課件
- 中醫(yī)館裝修合同范本
- 光伏電站小EPC規(guī)定合同范本
- 2024年01月江蘇2024年昆山鹿城村鎮(zhèn)銀行第三期校園招考筆試歷年參考題庫附帶答案詳解
- 《直播銷售》課件-項(xiàng)目一 認(rèn)識直播與直播銷售
- 建筑工程安全與管理
- 2025年內(nèi)蒙古機(jī)電職業(yè)技術(shù)學(xué)院高職單招職業(yè)技能測試近5年常考版參考題庫含答案解析
- 2024年05月齊魯銀行總行2024年社會招考筆試歷年參考題庫附帶答案詳解
- 浙江省紹興市2024-2025學(xué)年高一上學(xué)期期末調(diào)測英語試題(無答案)
- 幼兒園開學(xué)教師安全知識培訓(xùn)
- 《會展經(jīng)濟(jì)與策劃》課件
評論
0/150
提交評論