C++處理異常技巧-try,catch,throw,finally_第1頁(yè)
C++處理異常技巧-try,catch,throw,finally_第2頁(yè)
C++處理異常技巧-try,catch,throw,finally_第3頁(yè)
C++處理異常技巧-try,catch,throw,finally_第4頁(yè)
C++處理異常技巧-try,catch,throw,finally_第5頁(yè)
已閱讀5頁(yè),還剩93頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、這兩天要處理一個(gè)異常的問(wèn)題,剛好查了些相關(guān)的資料。在網(wǎng)上看到了一個(gè)不錯(cuò)的貼 子,就轉(zhuǎn)了過(guò)來(lái),方便本人,以及來(lái)此旅游的朋友學(xué)習(xí)。源地址: nt.html?91983,1異常處理的基本思想是簡(jiǎn)化程序的錯(cuò)誤代碼,為程序鍵壯性提供一個(gè)標(biāo)準(zhǔn)檢測(cè)機(jī)制。也 許我們已經(jīng)使用過(guò)異常,但是你會(huì)是一種習(xí)慣嗎,不要老是想著當(dāng)我打開一個(gè)文 件的時(shí)候才用異常判斷一下,我知道對(duì)你來(lái)說(shuō)你喜歡用return value 或者是printerror message 來(lái)做,你想過(guò)這樣做會(huì)導(dǎo)致Memory Leak,系統(tǒng)退出,代碼重復(fù) /難讀,垃圾一堆 .嗎?現(xiàn)在的軟件已經(jīng)是n*365*24小時(shí)的運(yùn)行了,軟件的健壯已經(jīng)是一個(gè)很要考慮

2、的時(shí)候了。自序:對(duì)寫程序來(lái)說(shuō)異常真的是很重要,一個(gè)穩(wěn)健的代碼不是靠返回ErrorMessage/return Value來(lái)解決的,可是往往我們從C走過(guò)來(lái),習(xí)慣了這樣的方式。僅 以本文獻(xiàn)給今天將要來(lái)臨的流星雨把,還好我能在今天白天把這寫完,否則會(huì)是 第4個(gè)通宵了;同時(shí)感謝Jeffrey大師,沒有他的SEH理論這篇文章只能完成一半,而且所有SEH列子的構(gòu)想都來(lái)自他的指導(dǎo);另外要感謝 Scott Meyers 大師,我是 看他的書長(zhǎng)大的;還要感謝Adamc / Darwin / Julian,當(dāng)然還有 Nick的Coffee內(nèi)容導(dǎo)讀:(請(qǐng)打開文檔結(jié)構(gòu)圖來(lái)讀這篇文章。)本文包括2個(gè)大的異常實(shí)現(xiàn)概念:C

3、+的標(biāo)準(zhǔn)異常和 SHE異常。C+ 標(biāo)準(zhǔn)異常:也許我們了解過(guò)他,但你有考慮過(guò),其實(shí)你根本不會(huì)使用,你不相 信,那我問(wèn)你:垃圾回收在C+中怎么實(shí)現(xiàn)?其實(shí)不需要實(shí)現(xiàn), C+已經(jīng)有了,但是你不會(huì)用,那么從 <構(gòu)造和析構(gòu)中的異常拋出 >開始看把。也許很高興看到錯(cuò)誤之 后的Heap/Stack中對(duì)象被釋放,可是如果沒有呢?有或者試想一下一個(gè)能解決的錯(cuò)誤,需要我們把整個(gè)程序Kill掉嗎?在C+標(biāo)準(zhǔn)異常中我向你推薦這幾章:< 使用異常規(guī)格編程 > < 構(gòu)造和析構(gòu)中的異常拋出 > <使用析構(gòu)函數(shù)防止資源泄漏 > 以及一個(gè)深點(diǎn)的 <拋出一個(gè)異 常的行為>

4、SHE異常:我要問(wèn)你你是一個(gè) WIN32程序員嗎?如果不是,那么也許你真的不需要看這 塊內(nèi)容了,SHE是Windows 的結(jié)構(gòu)化異常,每一個(gè) WIN32程序員都應(yīng)該要掌 握它。SHE 功能強(qiáng)大,包括 Termination handling和 Exception handling兩大部分,強(qiáng)有力的維護(hù)了代碼的健壯,雖然要以部分系統(tǒng)性能做犧牲(其實(shí)可以避免)。在SHE中有大量的代碼,已經(jīng)在 Win平臺(tái)上測(cè)試過(guò) 了。這里要提一下:在 _fin ally 處理中編譯器參與了絕大多數(shù)的工作,而Exception 則是OS接管了幾乎所有的工作,也許我沒有提到的是:對(duì)_finally 來(lái)說(shuō)當(dāng)遇到ExitT

5、hread/ExitProcess/abort等函數(shù)時(shí),fin ally 塊不會(huì)被執(zhí)行。另,我們的代碼使用軟件異常是比 return error message 好2*32的方法。另,使用析構(gòu)函數(shù)防止資源泄漏這個(gè)節(jié)點(diǎn)引用了 More effective C+ 的條款9 ,用2個(gè)列子,講述了我們一般都會(huì)犯下的錯(cuò)誤,往往這種錯(cuò)誤是我們沒有意識(shí)到的但確實(shí)是會(huì)給我們的軟件帶來(lái)致命的Leak/Crash ,但這是有解決的方法的,那就是使用靈巧指針llo如果對(duì)照<More effective C+> 的37條條款,關(guān)于異常的高級(jí)使用,有以下內(nèi)容是沒有完成的:l使用構(gòu)造函數(shù)防止資源Leak (

6、More effective C+ #10)l禁止異常信息傳遞到析構(gòu)Function 夕卜(More effective C+ #11)l通過(guò)引用捕獲異常(More effective C+ #13)I謹(jǐn)慎使用異常規(guī)格(More effective C+ #14)l 了解異常處理造成的系統(tǒng)開銷(More effective C+ #15)l 限制對(duì)象數(shù)量 (More effective C+ #26)l 靈巧指針(More effective C+ #28)聲明:節(jié)點(diǎn): < 使用析構(gòu)函數(shù)防止資源泄漏 > 和 節(jié)點(diǎn): < 拋出一個(gè)異常的行為 > 中 有大量的關(guān)于 More

7、 effective C+的條款,所以本文擋只用于自我閱讀和內(nèi)部交流,任何公開化和商業(yè)化,事先聲明與本人無(wú)關(guān)。C+異常C+引入異常的原因C+ 新增的異常機(jī)制改變了某些事情,這些改變是徹底的,但這些改變也可能讓我 們不舒服。例如使用未經(jīng)處理的pointer 變的很危險(xiǎn),Memory/Resource Leak變的更有可能了(別說(shuō)什么 Memory 便宜了,那不是一個(gè)優(yōu)秀的程序員說(shuō)的話。), 寫出一個(gè)具有你希望的行為的構(gòu)造函數(shù)和析構(gòu)函數(shù)也變的困難(不可預(yù)測(cè)),當(dāng)然最危險(xiǎn)的也許是我們寫出的東東狗屁了,或者是速度變慢了。大 多數(shù)的程序員知道 Howto use exception來(lái)處理我們的代碼,可是

8、很多人并不是很重視異常的處理(國(guó)外的很多Code倒是處理的很好,Java的Exception 機(jī)制很不錯(cuò))。異常處理機(jī)制是解決某些問(wèn)題的上佳辦法,但同時(shí)它也引入了許多隱藏的控制流程;有時(shí)候,要正確無(wú)誤的使用它并不容易。在異常被throw后,沒有一個(gè)方法能夠做到使軟件的行為具有可預(yù)測(cè)性和可靠性(這句話不是我說(shuō)的,是 Jack Reeves 寫的 Coping with Exception 和 Herb Sutter 寫的Excepti on-Safe Generic Con tai ners中的。)一個(gè)沒有按照異常安全設(shè)計(jì)的程序想Run正常,是做夢(mèng),別去想沒有異常出現(xiàn)的可能,對(duì)C程序來(lái)說(shuō),使用E

9、rror Code 就可以了,為什么還要引入異常?因?yàn)楫惓2荒鼙?忽略。如果一個(gè)函數(shù)通過(guò)設(shè)置一個(gè)狀態(tài)變量或返回錯(cuò)誤代碼來(lái)表示一個(gè)異常狀態(tài),沒 有辦法保證函數(shù)調(diào)用者將一定檢測(cè)變量或測(cè)試錯(cuò)誤代碼。結(jié)果程序會(huì)從它遇到的異常狀態(tài)繼續(xù)運(yùn)行,異常沒有被捕獲,程序立即會(huì)終止執(zhí)行。在 C程序中,我們可以用int setjmp( jmp_buf env );和voidIongjmp( jmp_buf env, int value );這2個(gè)函數(shù)來(lái)完成和異常處理相識(shí)的功能,但是MSDN中介紹了在C+中使用longjmp 來(lái)調(diào)整stack時(shí)不能夠?qū)植康膶?duì)象 調(diào)用析構(gòu)函數(shù),但是對(duì)C+程序來(lái)說(shuō),析構(gòu)函數(shù)是重要的(我就

10、一般都把對(duì)象的Delete 放在析構(gòu)函數(shù)中)。所以我們需要一個(gè)方法:能夠通知異常狀態(tài),又不能忽略這個(gè)通知,并且Searchi ng the stack以便找到異常代碼時(shí),還要確保局部對(duì)象的析構(gòu)函數(shù)被Call 。而C+的異常處理剛好就是來(lái)解決這些問(wèn)題的。有的地方只有用異常才能解決問(wèn)題,比如說(shuō),在當(dāng)前上下文環(huán)境中,無(wú)法捕捉或確 定的錯(cuò)誤類型,我們就得用一個(gè)異常拋出到更大的上下文環(huán)境當(dāng)中去。還有,異常處 理的使用呢,可以使出錯(cuò)處理程序與通常I代碼分離開來(lái),使代碼更簡(jiǎn)潔更靈活。另外就是程序必不可少的健壯性了,異常處理往往在其中扮演著重要的角色。C+使用throw 關(guān)鍵字來(lái)產(chǎn)生異常,try關(guān)鍵字用來(lái)檢測(cè)

11、的程序塊,catch關(guān)鍵字用 來(lái)填寫異常處理的代碼。異??梢杂梢粋€(gè)確定類或派生類的對(duì)象產(chǎn)生。C+能釋放堆棧,并可清除堆棧中所有的對(duì)象。C+的異常和pascal不同,是要程序員自己去實(shí)現(xiàn)的,編譯器不會(huì)做過(guò)多的動(dòng)作。throw異常類編程拋出異常用throw ,女口:throw Excepti on Class( my throw );例句中,Exceptio nClass是一個(gè)類,它的構(gòu)造函數(shù)以一個(gè)字符串做為參數(shù)。也就是說(shuō),在throw 的時(shí)候,C+的編譯器先構(gòu)造一個(gè)ExceptionClass的對(duì)象,讓它作為throw 的值拋出去。同時(shí),程序返回,調(diào)用析構(gòu)??聪旅孢@個(gè)程序:#in clude &

12、lt;iostream.h>class Exceptio nClasschar* n ame;public:Excepti on Class(c onst char* n ame="default n ame")cout<<"C on struct "<<n ame<<e ndl;this->n ame=n ame;Excepti on Class()cout<<"Destruct "< <n ame<<e ndl;void mythrow()thro

13、w Exceptio nClass("my throw"); void mai n()Excepti on Class e("Test");trye.mythrow();catch(.)cout<< II *| <<endl;這是輸出信息:Con struct TestCon struct my throwDestruct my throw*Destruct my throw(這里是異常處理空間中對(duì)異常類的拷貝的析構(gòu))Destruct Test不過(guò)一般來(lái)說(shuō)我們可能更習(xí)慣于把會(huì)產(chǎn)生異常的語(yǔ)句和要throw的異常類分成不同的類來(lái)寫,下

14、面的代碼可以是我們更愿意書寫的:class Exceptio nClasspublic:Excepti on Class(c onst char* n ame="Excepti on Default Class") cout<<"Excepti on Class Con struct Strin g"<<e ndl;Excepti on Class()cout<<"Exception Class Destruct String"<<endl;void ReportError() cout

15、<<"Excepti on Class: This is Report Error Message"<<e ndl; ;class ArguClasschar* n ame;public:ArguClass(char* n ame="default n ame")cout<<"C on struct Strin g:"< <n ame<<e ndl;this->n ame=n ame;ArguClass()cout<<"Destruct Strin

16、 g:"< <n ame<<e ndl;void mythrow()void Exceptio nFun ctio n(argume nt)throw()throw Exceptio nClass("my throw");; _tmai n()ArguClass e("haha");try e.mythrow();catch(i nt)cout<<"lf This is Message display scree n. This is a Error!"<<e ndl;catc

17、h(Excepti on Class pTest)pTest.ReportError();catch(.) cout<<'朱*"<<e ndl;輸出 MessageCon struct Strin g:hahaExcepti on Class Con struct StringExcepti on Class Destruct StringExcepti on Class: This is Report Error MessageExcepti on Class Destruct StringDestruct Strin g:haha使用異常規(guī)格編程如

18、果我們調(diào)用別人的函數(shù),里面有異常拋出,用去查看它的源代碼去看看都有什么異 常拋出嗎?這樣就會(huì)很煩瑣。比較好的解決辦法,是編寫帶有異常拋出的函數(shù)時(shí),采 用異常規(guī)格說(shuō)明,使我們看到函數(shù)聲明就知道有哪些異常出現(xiàn)。異常規(guī)格說(shuō)明大體上為以下格式:void Exceptio nFun ctio n(argume ntthro>w(Exceptio nClass1,ExceptionClass2,.)所有異常類都在函數(shù)末尾的throw()的括號(hào)中得以說(shuō)明了,這樣,對(duì)于函數(shù)調(diào)用者來(lái)說(shuō),是一清二楚的。注意下面一種形式: 表明沒有任何異常拋出。而正常的void ExceptionFunction(argum

19、ent)則表示:可能拋出任何一種異常,當(dāng)然,也可能沒有異常,意義是最廣泛的。異常捕獲之后,可以再次拋出,就用一個(gè)不帶任何參數(shù)的throw 語(yǔ)句就可以了。構(gòu)造和析構(gòu)中的異常拋出這是異常處理中最要注意的地方了先看個(gè)程序,假如我在構(gòu)造函數(shù)的地方拋出異常,這個(gè)類的析構(gòu)會(huì)被調(diào)用嗎?可如果 不調(diào)用,那類里的東西豈不是不能被釋放了?#in clude <iostream.h>#in clude <stdlib.h>class Exceptio nClass1char* s;public:Exceptio nClass1()cout<<"Excepti on Cl

20、ass1()"<<e ndl;s=new char4;cout<<"throw a excepti on"<<en dl;throw 18;Exceptio nClass1()cout<<"Excepti on Class1()"<<e ndl;delete s;void mai n()tryExcepti on Class1 e;catch(.)結(jié)果為:Excepti on Class1() throw a excepti on在這兩句輸出之間,我們已經(jīng)給S分配了內(nèi)存,但內(nèi)存沒有被釋

21、放(因?yàn)樗窃谖鰳?gòu)函數(shù)中釋放的)。應(yīng)該說(shuō)這符合實(shí)際現(xiàn)象,因?yàn)閷?duì)象沒有完整構(gòu)造。為了避免這種情況,我想你也許會(huì)說(shuō):應(yīng)避免對(duì)象通過(guò)本身的構(gòu)造函數(shù)涉及到異常拋 出。即:既不在構(gòu)造函數(shù)中出現(xiàn)異常拋出,也不應(yīng)在構(gòu)造函數(shù)調(diào)用的一切東西中出現(xiàn) 異常拋出。但是在C+中可以在構(gòu)造函數(shù)中拋出異常,經(jīng)典的解決方案是使用STL的標(biāo)準(zhǔn)類auto_ptr 。其實(shí)我們也可以這樣做來(lái)實(shí)現(xiàn):在類中增加一個(gè)Init();以及UnInit();成員函數(shù)用于進(jìn)行容易產(chǎn)生錯(cuò)誤的資源分配工作,而真正的構(gòu)造函數(shù)中先將所有成員置為NULL,然后調(diào)用Init(); 并判斷其返回值/或者捕捉Init()拋出的異常,如果Init(); 失敗了,則

22、在構(gòu)造函數(shù)中調(diào)用 UnInit(); 并設(shè)置一個(gè)標(biāo)志位表明構(gòu)造失敗。UnInit() 中按照成員是否為 NULL進(jìn)行資源的釋放工作。那么,在析構(gòu)函數(shù)中的情況呢?我們已經(jīng)知道,異常拋出之后,就要調(diào)用本身的析構(gòu) 函數(shù),如果這析構(gòu)函數(shù)中還有異常拋出的話,則已存在的異常尚未被捕獲,會(huì)導(dǎo)致異 常捕捉不到。標(biāo)準(zhǔn)C+異常類C+有自己的標(biāo)準(zhǔn)的異常類。 一個(gè)基類:exception 是所有C+異常的基類。class exceptio n public:excepti on() throw();excepti on(const excepti on& rhs) throw();excepti on&

23、; operator=(c onst excepti on& rhs) throw();virtual excepti on() throw();virtual const char *what() const throw(); 下面派生了兩個(gè)異常類:logic_erro報(bào)告程序的邏輯錯(cuò)誤,可在程序執(zhí)行前被檢測(cè)到。run time_erro報(bào)告程序運(yùn)行時(shí)的錯(cuò)誤,只有在運(yùn)行的時(shí)候才能檢測(cè)到。以上兩個(gè)又分別有自己的派生類:由logic_erro 派生的異常類domain error報(bào)告違反了前置條件in valid_argume nt指出函數(shù)的一個(gè)無(wú)效參數(shù)length_error指出有一個(gè)

24、產(chǎn)生超過(guò) NPOS 長(zhǎng)度的對(duì)象的企圖(NPOS為size_t的最大可表現(xiàn)值out_of_ra nge報(bào)告參數(shù)越界bad_cast在運(yùn)行時(shí)類型識(shí)別中有一個(gè)無(wú)效的dyn amic_cast 表達(dá)式bad_typeid報(bào)告在表達(dá)式typeid(*p)中有一個(gè)空指針 P 由runtime_error 派生的異常ran ge_error報(bào)告違反了后置條件overflow_error 報(bào)告一個(gè)算術(shù)溢出bad_alloc報(bào)告一個(gè)存儲(chǔ)分配錯(cuò)誤使用析構(gòu)函數(shù)防止資源泄漏這部分是一個(gè)經(jīng)典和很平常就會(huì)遇到的實(shí)際情況,下面的內(nèi)容大部分都是從MoreEffective C+條款中得到的。假 設(shè),你正在為一個(gè)小動(dòng)物收容所編

25、寫軟件,小動(dòng)物收容所是一個(gè)幫助小狗小貓尋 找主人的組織。每天收容所建立一個(gè)文件,包含當(dāng)天它所管理的收容動(dòng)物的資料信 息,你的工作是寫一個(gè)程序讀出這些文件然后對(duì)每個(gè)收容動(dòng)物進(jìn)行適當(dāng)?shù)奶幚?appropriate process ing )。完成這個(gè)程序一個(gè)合理的方法是定義一個(gè)抽象類,ALA( "Adorable Little Animal"),然后為小狗和小貓建立派生類。一個(gè)虛擬函數(shù)processAdopti on分別對(duì)各個(gè)種類的動(dòng)物進(jìn)行處理:class ALA public:virtual void processAdoptio n() = 0;class Puppy: p

26、ublic ALA public:processAdopti ons沒有捕獲異常,所以異常將傳遞給processAdoptio ns 的調(diào)用virtual void processAdoptio n();;class Kitte n: public ALA public:virtual void processAdoptio n();你需要一個(gè)函數(shù)從文件中讀信息,然后根據(jù)文件中的信息產(chǎn)生一個(gè) puppy (小狗)對(duì)象或者kitten(小貓)對(duì)象。這個(gè)工作非常適合于虛擬構(gòu)造器 (virtual constructor),在條款25詳細(xì)描述了這種函數(shù)。為了完成我們的目標(biāo),我們這樣聲明函數(shù): 從s中

27、讀動(dòng)物信息,然后返回一個(gè)指針/指向新建立的某種類型對(duì)象ALA * readALA(istrea m& s);你的程序的關(guān)鍵部分就是這個(gè)函數(shù),如下所示:/還有數(shù)據(jù)時(shí),繼續(xù)循環(huán)file:/得到下一個(gè)動(dòng)物file:/處理收容動(dòng)物file:/刪除readALA返回的對(duì)象void processAdopti on s(istream& dataSource) while (dataSource) ALA *pa = readALA(dataSource); pa->processAdopti on();delete pa; 這個(gè)函數(shù)循環(huán)遍歷 dataSource 內(nèi)的信息,處理它所

28、遇到的每個(gè)項(xiàng)目。 唯一要記住的 一點(diǎn)是在每次循環(huán)結(jié)尾處刪除 ps。這是必須的,因?yàn)槊看握{(diào)用 readALA都建立一個(gè) 堆對(duì)象。如果不刪除對(duì)象,循環(huán)將產(chǎn)生資源泄漏?,F(xiàn)在考慮一下,如果 pa->processAdoption拋出了一個(gè)異常,將會(huì)發(fā)生什么?者。轉(zhuǎn)遞中,processAdoptio ns函數(shù)中的調(diào)用 pa->processAdopti on語(yǔ)句后的所有語(yǔ)句都被跳過(guò),這就是說(shuō)pa沒有被刪除。結(jié)果,任何時(shí)候pa->processAdoptio n拋出一個(gè)異常都會(huì)導(dǎo)致processAdoptio ns內(nèi)存泄漏。堵塞泄漏很容易,void processAdopti on s(

29、istream& dataSource)while (dataSource) ALA *pa = readALA(dataSource);try pa->processAdopti on();catch (.) /捕獲所有異常delete pa;/避免內(nèi)存泄漏/當(dāng)異常拋出時(shí)throw;/傳送異常給調(diào)用者delete pa;/避免資源泄漏/當(dāng)沒有異常拋出時(shí)但是你必須用try和catch對(duì)你的代碼進(jìn)行小改動(dòng)。更重要的是你必須寫雙份清除 代碼,一個(gè)為正常的運(yùn)行準(zhǔn)備,一個(gè)為異常發(fā)生時(shí)準(zhǔn)備。在這種情況下,必須寫兩個(gè)delete代碼。象其它重復(fù)代碼一樣,這種代碼寫起來(lái)令人心煩又難于維護(hù),而且

30、 它看上去好像存在著問(wèn)題。不論我們是讓processAdoptio ns正常返回還是拋出異常,我們都需要?jiǎng)h除 pa,所以為什么我們必須要在多個(gè)地方編寫刪除代碼呢?我們可以把總被執(zhí)行的清除代碼放入processAdoptions函數(shù)內(nèi)的局部對(duì)象的析構(gòu)函數(shù)里,這樣可以避免重復(fù)書寫清除代碼。因?yàn)楫?dāng)函數(shù)返回時(shí)局部對(duì)象總是被釋放,無(wú)論函數(shù)是如何退出的。(僅有一種例外就是當(dāng)你調(diào)用Iongjmp 時(shí)。Longjmp 的這個(gè)缺點(diǎn)是C+率先支持異常處理的主要原因)具體方法是用一個(gè)對(duì)象代替指針pa,這個(gè)對(duì)象的行為與指針相似。當(dāng)pointer-like(類指針)對(duì)象被釋放時(shí),我們能讓它的析構(gòu)函數(shù)調(diào)用delete。替

31、代指針的對(duì)象被稱為smart pointers(靈巧指針),下面有解釋,你能使得pointer-like對(duì)象非常靈巧。在這里,我們用不著這么聰明的指針,我們只需要一個(gè)poi nter-lik對(duì)象,當(dāng)它離開生存空間時(shí)知道刪除它指向的對(duì)象。寫出這樣一個(gè)類并不困難,但是我們不需要自己去寫。標(biāo)準(zhǔn)C+庫(kù)函數(shù)包含一個(gè)類模板,叫做auto_ptr ,這正是我們想要的。每一個(gè)auto_ptr 類的 構(gòu)造函數(shù)里,讓一個(gè)指針指向一個(gè)堆對(duì)象(heap object ),并且在它的析構(gòu)函數(shù)里刪除這個(gè)對(duì)象。 下面所示的是auto_ptr類的一些重要的部分:template<class T>class aut

32、o_ptr public:/保存ptr,指向?qū)ο?刪除ptr指向的對(duì)象/ raw ptr to objectauto_ptr(T *p = 0): ptr(p) auto_ptr() delete ptr; private:T *ptr;auto_ptr類的完整代碼是非常有趣的,上述簡(jiǎn)化的代碼實(shí)現(xiàn)不能在實(shí)際中應(yīng)用。(我們至少必須加上拷貝構(gòu)造函數(shù),賦值operator以及下面將要講到的poi nter-emulati ng函數(shù)),但是它背后所蘊(yùn)含的原理應(yīng)該是清楚的:用auto_ptr對(duì)象代替raw指針,你將不再為堆對(duì)象不能被刪除而擔(dān)心,即使在拋出異常時(shí),對(duì)象也能被及時(shí)刪除。(因?yàn)閍uto_ptr

33、的析構(gòu)函數(shù)使用的是單對(duì)象形式的delete,所以auto_ptr 不能用于指向?qū)ο髷?shù)組的指針。如果想讓auto_ptr類似于一個(gè)數(shù)組模板,你必須自己寫一個(gè)。在這種情況下,用vector代替array可能更好) auto_ptrtemplate<class T>class auto_ptr public:typedef T eleme nt_type;explicit auto_ptr(T *p = 0) throw();auto_ptr(c onst auto_ptr<T >& rhs) throw(); auto_ptr<T >& oper

34、ator=(auto_ptr<T>& rhs) throw(); auto_ptr();T& operator*() const throw();T *operator->() const throw();T *get() const throw();T *release() const throw();;使用 auto_ptr對(duì)象代替 raw 指針,processAdoptions女口下所示:void processAdopti on s(istream& dataSource)while (dataSource) auto_ptr<ALA&g

35、t; pa(readALA(dataSource);pa->processAdopti on();這個(gè)版本的processAdoptions在兩個(gè)方面區(qū)別于原來(lái)的processAdoptions函數(shù)。第一,pa被聲明為一個(gè) auto_ptr<ALA>對(duì)象,而不是一個(gè) raw ALA* 指針。第二,在循環(huán)的結(jié)尾沒有 delete語(yǔ)句。其余部分都一樣,因?yàn)槌宋鰳?gòu)的方式,auto_ptr 對(duì)象的行為就象一個(gè)普通的指針。是不是很容易。隱藏在auto_ptr后的思想是:用一個(gè)對(duì)象存儲(chǔ)需要被自動(dòng)釋放的資源,然后依靠對(duì)象的析構(gòu)函數(shù)來(lái)釋放資源,這種思想不只是可以運(yùn)用在指針上,還能用在其它資

36、源的分配和釋放上。想一下這樣一個(gè)在GUI程序中的函數(shù),它需要建立一個(gè)window 來(lái)顯式一些信息:/這個(gè)函數(shù)會(huì)發(fā)生資源泄漏,如果一個(gè)異常拋出void display In fo(c onst In formatio n& info)WINDOW_HANDLE w(createWi ndow();在w對(duì)應(yīng)的window中顯式信息destroyWi ndow(w);很 多window 系統(tǒng)有 C like 接口,使用象like createWindow和destroyWindow函數(shù)來(lái)獲取和釋放 window 資源。如果在 w對(duì)應(yīng)的 window 中顯示信息時(shí),一個(gè)異常被拋出,w所對(duì)應(yīng)的w

37、indow 將被丟失,就象其它動(dòng)態(tài)分配的資源一樣。解決方法與前面所述的一樣,建立一個(gè)類,讓它的構(gòu)造函數(shù)與析構(gòu)函數(shù)來(lái)獲取和釋放資源:file:/ 一個(gè)類,獲取和釋放一個(gè) win dow 句柄class Win dowHa ndle public:Win dowHa ndle(WINDOW_HANDLE han dle): w(ha ndle) Win dowHa ndle() destroyWi ndow(w); operator WINDOW_HANDLE() return w; / see belowprivate:WINDOW_HANDLE w;II下面的函數(shù)被聲明為私有,防止建立多個(gè)WI

38、NDOW_HANDLE 拷貝file:/有關(guān)一個(gè)更靈活的方法的討論請(qǐng)參見下面的靈巧指針Win dowHa ndle(co nst Win dowHa ndle &);Win dowHa ndle & operator* onst Win dowHa ndle&);這看上去有些象auto_ptr ,只是賦值操作與拷貝構(gòu)造被顯式地禁止(參見 More effective C+ 條款27 ),有一個(gè)隱含的轉(zhuǎn)換操作能把 WindowHandle 轉(zhuǎn)換為WINDOW_HANDLE。這個(gè)能力對(duì)于使用WindowHandle對(duì)象非常重要,因?yàn)檫@意味著你能在任何地方象使用raw WIN

39、DOW_HANDLE 一樣來(lái)使用WindowHandle。(參見 More effective C+ 條款5,了解為什么你應(yīng)該謹(jǐn)慎使用隱式類型轉(zhuǎn)換操作)通過(guò)給出的 WindowHandle類,我們能夠重寫 displayInfo函數(shù),如下所示:/如果一個(gè)異常被拋出,這個(gè)函數(shù)能避免資源泄漏void display In fo(c onst In formatio n& info)Win dowHa ndle w(createWi ndow();在w對(duì)應(yīng)的window中顯式信息即使一個(gè)異常在 displaylnfo內(nèi)被拋出,被createWindow 建立的window 也能被釋放。資源應(yīng)

40、該被封裝在一個(gè)對(duì)象里,遵循這個(gè)規(guī)則,你通常就能避免在存在異常環(huán)境里 發(fā)生資源泄漏。但是如果你正在分配資源時(shí)一個(gè)異常被拋出,會(huì)發(fā)生什么情況呢?例 如當(dāng)你正處于resource-acquiri ng類的構(gòu)造函數(shù)中。還有如果這樣的資源正在被釋放時(shí),一個(gè)異常被拋出,又會(huì)發(fā)生什么情況呢?構(gòu)造函數(shù)和析構(gòu)函數(shù)需要特殊的技術(shù)。你能在 More effective C+ 條款 10 和 More effective C+ 條款 11 中獲取 有關(guān)的知識(shí)。拋出一個(gè)異常的行為個(gè)人認(rèn)為接下來(lái)的這部分其實(shí)說(shuō)的很經(jīng)典,對(duì)我們理解異常行為/異??截愂呛苡袔椭?。條款12 :理解拋出一個(gè)異常I與傳遞一個(gè)參數(shù)I或調(diào)用一個(gè)虛函數(shù)

41、I間的差異從語(yǔ)法上看,在函數(shù)里聲明參數(shù)與在catch子句中聲明參數(shù)幾乎沒有什么差別:class Widget . ;/void f1(Widget w);void f2(Widget & w);void f3(co nst Widget& w);void f4(Widget *pw);void f5(co nst Widget *pw);catch (Widget w).catch (Widget& w).catch (const Widget & w).catch (Widget *pw) .catch (const Widget *pw) .file:/ 一

42、個(gè)類,具體是什么類在這里并不重要/ 一些函數(shù),其參數(shù)分別為/ Widget, Widget&,或/ Widget* 類型file:/ 一些catch 子句,用來(lái)file:/捕獲異常,異常的類型為/ Widget, Widget&, 或/ Widget*你因此可能會(huì)認(rèn)為用 throw拋出一個(gè)異常到catch子句中與通過(guò)函數(shù)調(diào)用傳遞一個(gè) 參數(shù)兩者基本相同。這里面確有一些相同點(diǎn),但是他們也存在著巨大的差異。讓我們先從相同點(diǎn)談起。你傳遞函數(shù)參數(shù)與異常的途徑可以是傳值、傳遞引用或傳 遞指針,這是相同的。但是當(dāng)你傳遞參數(shù)和異常時(shí),系統(tǒng)所要完成的操作過(guò)程則是完 全不同的。產(chǎn)生這個(gè)差異的原因是

43、:你調(diào)用函數(shù)時(shí),程序的控制權(quán)最終還會(huì)返回到函 數(shù)的調(diào)用處,但是當(dāng)你拋出一個(gè)異常時(shí),控制權(quán)永遠(yuǎn)不會(huì)回到拋出異常的地方。有這樣一個(gè)函數(shù),參數(shù)類型是Widget,并拋出一個(gè) Widget類型的異常:/ 一個(gè)函數(shù),從流中讀值到Widget中istream operator»(istrea m& s, Widget& w);void passA ndThrowWidget()Widget localWidget;cin >> localWidget;file:/ 傳遞 localWidget至U operatorthrow localWidget;/ 拋出 loca

44、lWidget 異常當(dāng) 傳遞localWidget至U函數(shù)operator里,不用進(jìn)行拷貝操作,而是把operator>> 內(nèi)的引用類型變量 w指向localWidget ,任何對(duì)w的操作實(shí)際上都施 加到localWidget上。這與拋出localWidget異常有很大不同。不論通過(guò)傳值捕獲異 常還是通過(guò)引用捕獲(不能通過(guò)指針捕獲這個(gè)異常,因?yàn)轭愋筒黄ヅ?都將進(jìn)行 lcalWidget的拷貝操作,也就說(shuō)傳遞到 catch子句中的是 localWidget的拷貝。必須這么做,因?yàn)楫?dāng)localWidget離開了生存空間后,其析構(gòu)函數(shù)將被調(diào)用。如果把localWidget 本身(而 不是

45、它的拷貝)傳遞給 catch子句,這個(gè)子句接收到的只是 一個(gè)被析構(gòu)了的 Widget , 一個(gè)Widget的尸體II。這是無(wú)法使用的。因此C+規(guī)范要求被做為異常拋出的對(duì)象必須被復(fù)制。即使被拋出的對(duì)象不會(huì)被釋放,也會(huì)進(jìn)行拷貝操作。例如如果 passA ndThrowWidget函數(shù)聲明localWidget為靜態(tài)變量(static ),void passA ndThrowWidget()file:/現(xiàn)在是靜態(tài)變量(static );直存在至程序結(jié)束cin >> localWidget;throw localWidget;當(dāng)拋出異常時(shí)仍將復(fù)制出file:/localWidget常,也不

46、能在 catch 塊中修改localWidget異常對(duì)象進(jìn)行強(qiáng)制復(fù)制拷貝,差異:拋出異常運(yùn)行速度比參數(shù)傳遞要慢。/象以前那樣運(yùn)行/ 仍將對(duì) localWidget 進(jìn)行拷貝操作的一個(gè)拷貝。這表示即使通過(guò)引用來(lái)捕獲異;僅僅能修改 localWidget 的拷貝。對(duì)這個(gè)限制有助于我們理解參數(shù)傳遞與拋出異常的第二個(gè)static Widget localWidget;有這樣一個(gè)函數(shù),參數(shù)類型是Widget,并拋出一個(gè) Widget類型的異常:有這樣一個(gè)函數(shù),參數(shù)類型是Widget,并拋出一個(gè) Widget類型的異常:usidc52010-09-19 00:08當(dāng)異常對(duì)象被拷貝時(shí),拷貝操作是由對(duì)象的 拷

47、貝構(gòu)造函數(shù)完成的。該拷貝 構(gòu)造函數(shù)是對(duì)象的靜態(tài)類型(static type )所對(duì)應(yīng)類的拷貝構(gòu)造函數(shù),而這個(gè)事實(shí)影響到你如何在catch塊中再拋出一個(gè)catch 塊,乍一看好像一樣:catch (Widget & w)/捕獲Widget異常/throw;catch (Widget & w)/處理異常/重新拋出異常,讓它繼續(xù)傳遞/捕獲Widget異常/處理異常/傳遞被捕獲異常的 拷貝throw w;這兩個(gè)catch塊的差別在于第一個(gè)catch塊中重新拋出的是當(dāng)前捕獲的異 常,而第二個(gè)catch塊中重新拋出的是當(dāng)前捕獲異常的一個(gè)新的拷貝 果忽略生成額外拷貝的系統(tǒng)開銷,這兩種方法還有

48、差異么? 當(dāng)然有。第一個(gè)塊中重新拋出的是當(dāng)前異常(current exception 它是什么類型。特別是如果這個(gè)異常開始就是做為SpecialWidget出的,那么第一個(gè)塊中傳遞出去的還是 靜態(tài)類型(static type )是 Widget 。/),無(wú)論 類型拋SpecialWidget 異常,即使 w的 這是因?yàn)橹匦聮伋霎惓r(shí)沒有進(jìn)行不是對(duì)象的動(dòng)態(tài)類型(dynamic type)對(duì)應(yīng)類的拷貝構(gòu)造函數(shù)。比如以下這經(jīng)過(guò)少許修改的passAndThrowWidget:class Widget . ;class SpecialWidget: public Widget . ;void passA

49、 ndThrowWidget()SpecialWidget localSpecialWidget;Widget& rw = localSpecialWidget;/ rw 引用 SpecialWidgetthrow rw;file:/ 它拋出一個(gè)類型為 Widget/的異常 這 里拋出的異常對(duì)象是 Widget,即使rw引用的是一個(gè)SpecialWidget 因?yàn)?rw 的靜態(tài)類型(static type )是 Widget,而不是 SpecialWidget 。 你的編譯器根本沒有主要到rw引用的是一個(gè)SpecialWidget 。編譯器所 注意的是rw的靜態(tài)類型(static ty

50、pe )。這種行為可能與你所期待的不 一樣,但是這與在其他情況下C+中拷貝構(gòu)造函數(shù)的行為是一致的。(不 過(guò)有一種技術(shù)可以讓你根據(jù)對(duì)象的動(dòng)態(tài)類型dynamic type進(jìn)行拷貝,參見條款25 異常是其它對(duì)象的拷貝, 異常。比如下面這兩個(gè)拷貝操作。第二個(gè)catch塊重新拋出的是新異常,類型總是Widget ,因?yàn)閣的靜態(tài)類型(static type )是Widget。一般來(lái)說(shuō),你應(yīng)該用throw來(lái)重新拋出當(dāng)前的異常,因?yàn)檫@樣不會(huì)改變被傳遞出去的異常類型,而且 更有效率,因?yàn)椴挥蒙梢粋€(gè)新拷貝。(順便說(shuō)一句,異常生成的拷貝是一個(gè)臨時(shí)對(duì)象。正如條款19解釋的,),拋出的:/通過(guò)傳值捕獲異常/通過(guò)傳遞引

51、用捕獲異常file:/通過(guò)傳遞指向const的引用臨時(shí)對(duì)象能讓編譯器優(yōu)化它的生存期(optimize it out of existe nee 不過(guò)我想你的編譯器很難這么做,因?yàn)槌绦蛑泻苌侔l(fā)生異常,所以編譯器 廠商不會(huì)在這方面花大量的精力。) 讓我們測(cè)試一下下面這三種用來(lái)捕獲 Widget異常的catch子句,異常是 做為 passAndThrowWidgetp catch (Widget w)./catch (Widget & w).catch (const Widget & w).file:/捕獲異常我 們立刻注意到了傳遞參數(shù)與傳遞異常的另一個(gè)差異。一個(gè)被異常拋出的對(duì)象(剛

52、才解釋過(guò),總是一個(gè)臨時(shí)對(duì)象)可以通過(guò)普通的引用捕獲;它不 需要通過(guò)指向 const對(duì)象的引用(reference-to-const)捕獲。在函數(shù)調(diào)用中不允許轉(zhuǎn)遞一個(gè)臨時(shí)對(duì)象到一個(gè)非const引用類型的參數(shù)里(參見條款19 ),但是在異常中卻被允許。讓我們先不管這個(gè)差異,回到異常對(duì)象拷貝的測(cè)試上來(lái)。我們知道當(dāng)用傳 值的方式傳遞函數(shù)的參數(shù),我們制造了被傳遞對(duì)象的一個(gè)拷貝(參見Effective C+ 條款22 ),并把這個(gè)拷貝存儲(chǔ)到函數(shù)的參數(shù)里。同樣我 們通過(guò)傳值的方式傳遞一個(gè)異常時(shí),也是這么做的。當(dāng)我們這樣聲明一個(gè) catch子句時(shí):catch (Widget w) ./ 通過(guò)傳值捕獲會(huì)建立兩個(gè)

53、被拋出對(duì)象的拷貝,一個(gè)是所有異常都必須建立的臨時(shí)對(duì)象, 第二個(gè)是把臨時(shí)對(duì)象拷貝進(jìn) w中。同樣,當(dāng)我們通過(guò)引用捕獲異常時(shí), catch (Widget& w) ./ 通過(guò)引用捕獲catch (const Widget& w) .file:/ 也通過(guò)引用捕獲這仍舊會(huì)建立一個(gè)被拋出對(duì)象的拷貝:拷貝是一個(gè)臨時(shí)對(duì)象。相反當(dāng)我們 通過(guò)引用傳遞函數(shù)參數(shù)時(shí),沒有進(jìn)行對(duì)象拷貝。當(dāng)拋出一個(gè)異常時(shí),系統(tǒng) 構(gòu)造的(以后會(huì)析構(gòu)掉)被拋出對(duì)象的拷貝數(shù)比以相同對(duì)象做為參數(shù)傳遞 給函數(shù)時(shí)構(gòu)造的拷貝數(shù)要多一個(gè)。我 們還沒有討論通過(guò)指針拋出異常的情況,不過(guò)通過(guò)指針拋出異常與通過(guò)指針傳遞參數(shù)是相同的。不論哪種方法都

54、是一個(gè)指針的拷貝被傳遞。你不 能認(rèn)為拋出的指針是 一個(gè)指向局部對(duì)象的指針,因?yàn)楫?dāng)異常離開局部變量 的生存空間時(shí),該局部變量已經(jīng)被釋放。Catch子句將獲得一個(gè)指向已經(jīng)不存在的對(duì)象的指針。這種行為在設(shè)計(jì)時(shí)應(yīng)該予以避免。對(duì)象從函數(shù)的調(diào)用處傳遞到函數(shù)參數(shù)里與從異常拋出點(diǎn)傳遞到 catch子句 里所采用的方法不同,這只是參數(shù)傳遞與異常傳遞的區(qū) 別的一個(gè)方面,第 二個(gè)差異是在函數(shù)調(diào)用者或拋出異常者與被調(diào)用者或異常捕獲者之間的類型匹配的過(guò)程不同。比如在標(biāo)準(zhǔn)數(shù)學(xué)庫(kù)(the sta ndard math library中sqrt函數(shù):double sqrt(double);/ from <cmath&g

55、t; or <math.h>我們能這樣計(jì)算一個(gè)整數(shù)的平方根,如下所示:int i;double sqrtOfi = sqrt(i);毫無(wú)疑問(wèn),C+允許進(jìn)行從int到double的隱式類型轉(zhuǎn)換,所以在sqrt 的調(diào)用中,i被悄悄地轉(zhuǎn)變?yōu)閐ouble類型,并且其返回值也是double 。(有關(guān)隱式類型轉(zhuǎn)換的詳細(xì)討論參見條款5) 一般來(lái)說(shuō),catch子句匹配異常類型時(shí)不會(huì)進(jìn)行這樣的轉(zhuǎn)換。見下面的代碼:void f(i nt value)/ 如果 someFunction()返回 file:/真,拋出一個(gè)整形值try if (someF unction() throw value;/只處理

56、double類型的異常catch (double d) 在try塊中拋出的int異常不會(huì)被處理double異常的catch子句捕獲。該 子句只能捕獲真真正正為double類型的異常;不進(jìn)行類型轉(zhuǎn)換。因此如 果要想捕獲int異常,必須使用帶有int或int&參數(shù)的catch子句。不過(guò)在catch子句中進(jìn)行異常匹配時(shí)可以進(jìn)行兩種類型轉(zhuǎn)換。第一種是繼 承類與基類間的轉(zhuǎn)換。一個(gè)用來(lái)捕獲基類的catch子句也可以處理派生類類型的異常。例如在標(biāo)準(zhǔn)C+庫(kù)(STL )定義的異常類層次中的診斷部 分(diagnostics portion)(參見 Effective C+ 條款 49)。捕獲runtime_errors 異常的Catch子句可以捕獲range_error 類型和 overflow_error 類型的異常,可以接收根類 exception 異常的catch子

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論