C++中的異常處理_第1頁
C++中的異常處理_第2頁
C++中的異常處理_第3頁
C++中的異常處理_第4頁
C++中的異常處理_第5頁
已閱讀5頁,還剩11頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、異常處理(一)簡介大型應用軟件往往是分層構(gòu)建的。在最底層你會發(fā)現(xiàn)庫函數(shù),API函數(shù),和私有的底層函數(shù)。然而在最高層則是用戶接口組件,比如一個電子制表軟件讓用戶填寫數(shù)據(jù)表單。下面來看一種普通的航空訂票系統(tǒng):它的最高端是由一些GUI組件所組成,用來在用戶的屏幕上顯示內(nèi)容。這些高端組件與那些封裝了數(shù)據(jù)庫API的數(shù)據(jù)存取對象相互作用。再往底層一些,那些數(shù)據(jù)庫API與數(shù)據(jù)庫引擎相交互,然而數(shù)據(jù)庫引擎自己又會調(diào)用系統(tǒng)服務來處理底層的硬件資源,比如物理內(nèi)存,文件系統(tǒng)和安全模型。一般情況下,及其嚴格的運行期錯誤會在這些底層代碼中被檢測出來,但是它們不能-或者說不應該-試圖自己處理這些錯誤。解決這些嚴格的運行期

2、錯誤的責任應該由高端組件來承擔。為了解決一個錯誤,高端組件必須得到錯誤發(fā)生的通知。本質(zhì)上,錯誤處理包括錯誤檢測和通知高端組件。這些組件依次處理錯誤并且試圖從錯誤中恢復。傳統(tǒng)的錯誤處理方法在早些時期,C+本身并沒有處理運行期錯誤的能力。取而代之的是那些傳統(tǒng)的C方法。這些方法可以被歸為三類設(shè)計策略:1) 返回一個狀態(tài)碼來表明成功或失敗2) 把錯誤碼賦值給一個全局標記并且讓其他的函數(shù)來檢測3) 終止整個程序上述的任何一個方法在面向?qū)ο蟓h(huán)境下都有明顯的缺點和限制。其中的一些根本就不可接受,尤其是在大型應用程序中。接下來的部分將會仔細檢查一下這些方法,目的是發(fā)現(xiàn)他們與生俱來的限制和危險性。返回一個錯誤碼

3、在某種程度上這個方法是有用的,比如一個小型程序有著一致而且有限的錯誤碼存在,并且嚴格的報告錯誤和檢查一個函數(shù)返回值的策略被應用。然而,這種方法也有著顯著的局限性;例如,錯誤類型和它們的列舉值必須標準化。因為一個庫的實現(xiàn)者可能選擇返回值0來代表一個錯誤,然而另一個實現(xiàn)者卻選擇0來代表成功并且用那些非0值代表出現(xiàn)錯誤。通常,那些返回碼會在一個公共頭文件中以符號常量的形式存在,從而在整個軟件的開發(fā)過程中或者在一個開發(fā)團隊里達成一致。但是,這些碼并不是標準的。不用說,在結(jié)合那些不兼容的軟件庫的時候,如何處理非標準的錯誤碼將會是一件極其頭疼的事。另外一個缺點是對于每一個返回碼都必須查閱和解釋-一個乏味并

4、且昂貴的操作。這個策略的實現(xiàn)需要調(diào)用者在每一次調(diào)用的時候?qū)Ψ祷刂颠M行檢查,如果沒有這樣做將會導致運行期錯誤。當一個錯誤碼被檢測,就會終止正常的執(zhí)行流程并且把錯誤碼傳遞給調(diào)用者。那些附加的包裹每一個函數(shù)調(diào)用的代碼會很輕易的使程序的大小翻倍并且引起軟件維護和程序可讀性的降低。更糟的是,有時要想返回一個error value是不可能的。例如,構(gòu)造函數(shù)沒有返回值,所以就不能應用這種方法在對象構(gòu)造失敗的情況下報告錯誤。求助于全局標記一個可選的用來報告運行期錯誤的途徑是使用全局標記,它表明了最后的操作是否成功。不像返回碼策略,這個方法是標準化的。C 的<errno.h>頭文件中定義了一種機制用

5、來檢查和給一個全局整型標記errno賦值。這種策略固有的缺陷也是不能被忽視的。在一個多線程環(huán)境中,被一個線程賦予了一個錯誤碼的errno有可能不經(jīng)意的被另一個線程所改寫,而調(diào)用者還未對errno進行檢查。另外,對錯誤碼而不是一個更為可讀的信息的使用是很不利的,因為那些錯誤碼可能會在不同的環(huán)境中不兼容。最終,這種方法需要嚴格的良好的編程樣式,也就是不斷的對errno的當前值進行檢查。全局標記策略和函數(shù)返回值策略是相似的:二者都提供一種機制來報告錯誤,但是二者卻都不能保證錯誤被處理。例如,一個函數(shù)沒有成功打開一個文件可以通過給errno賦予一個合適的值來表明錯誤的發(fā)生。然而,它不能阻止另一個函數(shù)試

6、圖寫入和關(guān)閉那個文件。更進一步,如果errno表明一個錯誤并且程序員檢測到而且按照預期處理了它,那么errno還應該被顯式的復位。如果一個程序員忘記了做這件事,那么將會引起其他函數(shù)誤以為錯誤還沒有被處理,從而去校正那個問題,引起不可預知的結(jié)果。終止程序最為殘酷的處理運行期錯誤的方法是簡單的終止程序。這種解決方案去除了上面兩種方法的一些缺點;例如,沒有必要反復的檢查每個函數(shù)返回值的狀態(tài),而且程序員也不必賦值給一個全局標記,反復的測試和清除它的值。在標準C的函數(shù)庫中有兩個函數(shù)用來終止一個程序:exit()和abort()。exit()被調(diào)用能夠表明程序被成功終止,或者它可以在遇到運行期錯誤的時候被

7、調(diào)用。在把控制權(quán)交還給運行環(huán)境之前,exit()首先會清空流和關(guān)閉打開的文件。abort()卻不一樣,它表示程序被意外終止,不會清空流和關(guān)閉打開的文件。關(guān)鍵性的程序不應該在任何運行期錯誤存在的情況下突然終止。如果一個生命支持系統(tǒng)突然停止工作僅僅是因為它的控制器檢測到0做除數(shù),那么將是一種災難。同樣,一個控制由人駕駛的航天飛機自動運行的計算機系統(tǒng)也不應該因為暫時的和地面控制系統(tǒng)失去聯(lián)系就停止工作。類似的,電話公司的賬目系統(tǒng)或者銀行系統(tǒng)都不應該在運行期錯誤出現(xiàn)的時候就中止。健壯的真實世界的應用程序應該做的更好。程序終止甚至對于應用程序都是有問題的。一個檢測到錯誤的函數(shù)通常都沒有必要的信息來衡量錯誤

8、的嚴重性。例如一個內(nèi)存分配函數(shù)并不能說出內(nèi)存分配失敗是由于用戶正在使用調(diào)試器,網(wǎng)頁瀏覽器,電子制表軟件,文字處理軟件,還是由于系統(tǒng)因為硬件錯誤變得不穩(wěn)定。在第一種情況下,系統(tǒng)可以簡單的顯示一條信息來告訴用戶關(guān)閉不必要的應用程序。第二種情況下,就需要一種更為殘酷的措施了。然而,在終止程序的策略下,那個內(nèi)存分配函數(shù)就會簡單的終止程序,而不考慮錯誤的嚴重性。這種方法在一些關(guān)鍵性應用程序中是無法應用的。好的系統(tǒng)設(shè)計應該保證運行期錯誤被檢測和報告,但是它也應該確保最小限度的容錯水平。終止程序在極限環(huán)境下或者在調(diào)試階段是可以被接受的。然而,abort()和exit()卻不應該在面向?qū)ο蟓h(huán)境中使用,甚至即使

9、在調(diào)試階段,因為他們并沒有意識到C+對象模型的存在。exit()和abort()不銷毀對象對象可以持有從構(gòu)造函數(shù)或者某個成員函數(shù)中獲得的資源:從free store中分配的內(nèi)存,文件句柄,通信端口,I/O設(shè)備等等。這些資源必須在適當時候被釋放。通常,資源都是由析構(gòu)函數(shù)來釋放。這種設(shè)計方法被稱為資源獲取即初始化(resource initialization is acquisition)。在棧上建立的局部對象會自動銷毀。然而abort() 和exit()并不調(diào)用這些局部對象的析構(gòu)函數(shù)。因此,程序的意外終止將會引起無法挽回的損害:數(shù)據(jù)庫被破壞,文件可能丟失,并且一些有價值的數(shù)據(jù)可能丟失?;谶@個

10、原因,請不要在面向?qū)ο蟓h(huán)境中使用abort()和exit()。                               異常處理(二)進入異常處理正如你所見,傳統(tǒng)C的錯誤處理方法并不適合C+,C+的一個設(shè)計目標就是讓用C+進行大規(guī)模軟件開發(fā)比C更好更安全。C+的設(shè)計者們已經(jīng)意識到缺乏合適的錯誤處

11、理機制使得實現(xiàn)這一目標相當?shù)睦щy。他們試圖尋找一種完全擺脫C的錯誤處理缺陷的解決方案。其中的一種想法就是建立在當異常被觸發(fā)的時候程序自動把控制權(quán)傳遞給系統(tǒng)。機制必須簡單,并且它能夠使程序員從不斷的檢查一個全局標記或者返回值的苦差事中解脫出來。另外,它還必須保證異常處理程序能夠自動獲得異常信息。最終它還要確保當一個異常沒有在本地處理的時候,本地對象能夠被適當?shù)匿N毀,并且把它所持有的資源釋放。1989年,在多年的研究和多方建議下,異常處理被引入C+。C+并不是第一個對結(jié)構(gòu)化運行期錯誤處理進行支持的語言。早在20世紀60年代,PL/1就提供了一種內(nèi)建的異常處理機制;Ada也在20世紀80年代提供了自

12、己的異常處理,另外還有幾種語言也做到了這一點。但是這些異常處理模型沒有一個適合C+對象模型和程序結(jié)構(gòu)。因此,被提議的C+異常處理是獨一無二的,并且它已經(jīng)作為了一種模型出現(xiàn)在一些新產(chǎn)生的語言之中。異常處理機制的實現(xiàn)被證明是一種挑戰(zhàn)。第一個C+編譯器,cfront,在UNIX環(huán)境下運行。和許多UNIX編譯器一樣,它首先是作為一個翻譯器把C+代碼轉(zhuǎn)換成C,接著再編譯C代碼。Cfront 4.0計劃引入異常處理,然而,異常處理機制的實現(xiàn)是如此的復雜,以至于cfront 4.0的開發(fā)團隊在用了一年時間設(shè)計它之后完全的放棄了這個項目。Cfront 4.0再也沒有出臺。然而,異常處理卻成為了標準C+的有機組

13、成部分。后來出現(xiàn)的一些編譯器都支持了它。在接下來的部分里將會解釋為什么在cfront以及任何編譯器下實現(xiàn)異常處理是如此的困難。實現(xiàn)異常處理所面臨的挑戰(zhàn)實現(xiàn)異常處理所遇到的困難主要來自于以下幾個因素:第一, 實現(xiàn)必須保證對應于某一異常的合適的handler被找到。第二, 異常對象必須是多態(tài)的;這樣,當實現(xiàn)無法通過派生類對象定位handler的時候可以考慮基類的handler。這種需要表明必須引入運行期類型檢測。第三, 作為一個附加的復雜性,實現(xiàn)必須能夠調(diào)用所有局部對象的析構(gòu)函數(shù)。這個過程被稱為解棧(stack unwinding)。因為早期的C+編譯器首先要把C+源文件轉(zhuǎn)換為純C,然后再把C代碼

14、編譯成機器碼。異常處理的實現(xiàn)者們不得不用C來實現(xiàn)運行期類型鑒別和stack unwinding。幸運的是,這些障礙已經(jīng)被克服。應用異常處理異常處理是一種靈活并且精巧的工具。它克服了C的傳統(tǒng)錯誤處理方法的缺點并且能夠被用來解決一系列運行期錯誤。但是,異常處理也像其他語言特性一樣,很容易被誤用。為了能夠有效的使用這一特性,理解運行期機制是如何工作的以及相關(guān)的性能開銷是非常重要的。接下來的部分里將會進入異常處理的內(nèi)部并且討論如何使用這一工具來建立安全的應用系統(tǒng)。異常處理要素異常處理是一種把控制權(quán)從異常發(fā)生的地點轉(zhuǎn)移到一個匹配的handler的機制。異常是內(nèi)建數(shù)據(jù)類型變量或者是對象。異常處理機制包括四

15、個部分:a try block,一個或多個和try block相關(guān)的handler,throw表達式,以及異常自己。Try block包含可能拋出異常的代碼。例如:try  int * p = new int1000000; /may throw std:bad_alloc一個try block后面將跟有一個或多個catch語句或者說是handlers, 每一個handler 處理不同類型的異常。例如:try int * p = new int1000000; /may throw std:bad_alloc /.catch(std:bad_alloc&

16、) catch (std:bad_cast&)handler僅僅被在try block中的throw表達式以及函數(shù)所調(diào)用。throw表達式包括一個關(guān)鍵字throw以及assignment expression。例如:try  throw 5; / 5 is assigned to n in the following catch statementcatch(int n) throw表達式和返回語句很相似。empty throw是沒有操作數(shù)的throw語句。例如:throw;在handler中的empty throw表明它在重新拋出異常,后面我們會討論到它。另外,如果目前沒有

17、異常被處理,那么執(zhí)行一個empty throw將會調(diào)用terminate()。Stack Unwinding 當一個異常被拋出,運行時機制首先在當前的作用域?qū)ふ液线m的handler。如果不存在這樣一個handler,那么將會離開當前的作用域,進入更外圍的一層繼續(xù)尋找。這個過程不斷的進行下去直到合適的handler被找到為止。此時堆棧已經(jīng)被解開,并且所有的局部對象被銷毀。如果始終都沒有找到合適的handler,那么程序?qū)K止。注意,C+保證局部對象被適當?shù)匿N毀僅僅是在拋出的異常被處理的情況下。一個未被撲獲得異常是否引起局部對象的銷毀由實現(xiàn)決定的。為了保證局部對象的析構(gòu)函數(shù)在異常未被捕獲情況下也

18、能夠被正常調(diào)用,你應該在main()里加入捕獲任何異常的catch語句。例如:int main()  try      /.    catch(std:exception& stdexc)   / handle expected exceptions      /.    catch(.)   / ensure proper cleanup in the case of an uncaught exception

19、60;     return 0;stack unwinding的過程就好比一個返回語句序列,每一個都返回相同的對象給它的調(diào)用者。                                     

20、60;異常處理(三)傳遞異常對象給handler一個異常能夠按值或者按引用的方式傳遞給它的handler。為異常對象分配的內(nèi)存是通過一種未被定義的途徑(但是并沒有在自由存儲區(qū))。一些實現(xiàn)使用專門的異常堆棧,在那里,異常對象被創(chuàng)建。當一個異常按引用的方式傳遞,handler獲得的是在異常堆棧上建立的對象的引用。通過引用方式傳遞異常保證了它的多態(tài)行為。按值傳遞的異常被建立在調(diào)用者的堆棧上。例如:#include  <cstdio>class ExBase /*.*/;class FileEx: public ExBase /*.*/;void Write(FILE *pf)&

21、#160; if (pf = NULL) throw FileEx();  /. process pf normallyint main ()  try      Write(NULL); /will cause a FileEx exception to be thrown    catch(ExBase& exception) /catch ExBase or any object derived from it    /diagnostics and remedies &

22、#160; 按值傳遞異常將會造成反復的復制對象,并且它的花費是昂貴的,因為異常對象在匹配的handler被找到以前會被構(gòu)造和銷毀許多次。然而,在比較罕見的情況下也會發(fā)生按值傳遞,由于為了保持應用系統(tǒng)的整體性,性能考慮往往被放在了第二位。異常類型匹配異常的類型決定了哪個handler能夠捕獲它。異常的匹配規(guī)則比函數(shù)重載的匹配規(guī)則更為嚴格??紤]下面這種情況:try  throw int();catch (unsigned int) /will not catch the exception from the previous try-block   拋出異常的類型是i

23、nt型,然而handler卻期待一個unsigned int。異常處理機制不認為二者是能夠匹配的類型;結(jié)果,拋出的異常沒有被捕獲。異常匹配規(guī)則僅僅允許一個非常有限的轉(zhuǎn)換集。對于一個異常E和一個帶有T或T&參數(shù)的handler,符合下面的條件可以進行匹配:T和E是同一類型(const 和volatile被忽略)T是E的沒有歧義的公共基類如果E和T都是指針類型,當二者的類型相同時可以進行匹配或者E所指向?qū)ο蟮念愋蜔o歧義的公有繼承自T指向?qū)ο蟮念愋?。作為對象的異常正如你所發(fā)現(xiàn)的,傳統(tǒng)的通過返回一個整型錯誤碼的方法在OOP中已經(jīng)不再適用。C+異常處理機制提供了更多的彈性,安全性和穩(wěn)固性。一個異

24、常既可以是int 或char等基本類型,也可以是更為豐滿的對象,有著數(shù)據(jù)成員和成員函數(shù)。這樣一個對象可以為handler提供更多的選擇進行恢復。一個聰明的異常對象,可以通過成員函數(shù)返回錯誤的詳細描述,而不是讓handler查閱某個表或文件。它也可以擁有在錯誤被適當處理之后使程序從運行期錯誤中恢復的成員函數(shù)??紤]有這樣一個日志類想要添加新的紀錄到一個已存在的日志文件中:如果打開日志文件失敗,它會拋出一個異常。當它被匹配的handler所捕獲,異常對象能夠擁有一個成員函數(shù),這個成員函數(shù)建立一個對話框。操作者可以從對話框中選擇恢復方法,包括建立一個新的日志文件,選擇另一個日志文件,或者是允許系統(tǒng)在沒

25、有日志的情形下運行。Exception Specification一個函數(shù)可以通過指定一個它所能拋出的異常列表來提醒它的用戶。Exception specifications在用戶只能看到函數(shù)的原型但是卻無法獲得它的源文件的時候?qū)值挠杏?。下面是一個指定異常的例子:class Zerodivide/*.*/;int divide (int, int) throw(Zerodivide);   / function may throw an exception         

26、60;                                 / of type Zerodivide, but no other如果你的函數(shù)永遠不會拋出任何異常,它可以像下面這樣聲明:bool equals (int, int) throw(); /no except

27、ion is thrown from this function注意一個函數(shù)被聲明為沒有exception specification例如:bool equals (int, int);Exception specification在運行期生效一個exception specification不會在編譯期被檢查,而是在運行期。當一個函數(shù)試圖拋出一個在exception specification中未被指定的異常的時候,異常處理機制將會檢測到這種違規(guī)并且調(diào)用標準函數(shù)unexpected()。unexpected()的默認行為是調(diào)用terminate()終止程序。違背exception speci

28、fication就好比是一個bug,不應該發(fā)生,這就是為什么默認行為是終止程序。不過默認的行為也可以被改變,通過使用函數(shù)set_unexpected()。因為exception specifications在運行期才有效,所以編譯期可能會故意忽略那些違背exception specifications的代碼。好比下面:int f();    / no exception specification, f can throw any type of exceptionvoid g(int j) throw()    / g promis

29、es not to throw any exception at all  int result = f(); / if f throws an exception, g will violate its guarantee                    /not to throw an exception. still, this code is legal在上面這個例子中,函數(shù)g()并

30、不允許拋出任何異常。它調(diào)用函數(shù)f(),然而f()卻可能拋出任何異常因為它沒有exception specification。如果f()拋出一個異常,它將會通過g()傳播出去,但是這卻破壞了g()不會拋出任何異常的保證。這也許看起來會很奇怪,有一些違規(guī)在編譯期就應該被發(fā)現(xiàn)報錯的,為什么一定要等到運行期呢?然而許多問題并不像想象的那么簡單,以下幾個原因就要求必須采用運行期檢測策略。在前面的那個程序中,f()可能是一個老式的C函數(shù)。我們不可能強迫每個C函數(shù)有exception specification。并且因為這個原因就強迫程序員在g()中寫不必要的try和catch()塊也是不實際的。通過強迫e

31、xception specification只在運行期才有效,C+采取了“信任程序員”的策略而不是強加負擔給程序員和實現(xiàn)。Exception specification的一致性C+需要派生類中的exception specification與基類保持一致。這意味著派生類重載的virtual function的exception specification必須是基類的限制性子集,例如:/ various exception classesclass BaseEx;class DerivedEx: public BaseEx;class OtherEx ;class Apublic: 

32、virtual void f() throw (BaseEx);  virtual void g() throw (BaseEx);  virtual void h() throw (DerivedEx);  virtual void i() throw (DerivedEx);  virtual void j() throw(BaseEx);class D: public Apublic:   void f() throw (DerivedEx); /OK, DerivedEx is derived from BaseEx  voi

33、d g() throw (OtherEx);  /error; exception specification is                              /incompatible with A's  void h() throw (DerivedEx); /OK, i

34、dentical to the exception                               /specification in base  void i() throw (BaseEx); /error, BaseEx is not a DerivedEx nor is it&#

35、160;                          /derived from DerivedEx  void j()  throw (BaseEx,OtherEx); /error, less restrictive than the       &

36、#160;                            /specification of A:j;相同的一致性限制也應用于函數(shù)指針。一個擁有exception specification函數(shù)指針只能被賦予一個有著相同或更為局限的exception specification的函數(shù)。這說明一個沒有exception s

37、pecification的函數(shù)指針不能被賦予一個有exception specification的函數(shù)。注意,因為exception specification不能被認為是函數(shù)類型的一部分,因此你不能聲明兩個僅僅是exception specification不同的函數(shù)。例如:void f(int) throw (Y);void f(int) throw (Z); /error; redefinition of 'void f(int)'同樣的原因,聲明一個包含exception specification的typedef也是錯誤的:typedef void (*PF) (in

38、t) throw(Exception); / error                                        異常處理(四)在對象構(gòu)造和銷毀時出現(xiàn)異常構(gòu)造函數(shù)和析構(gòu)函數(shù)被自動調(diào)用,并且

39、它們不能夠利用返回值來表明發(fā)生運行期錯誤。從表面上看,在對象構(gòu)造和銷毀時拋出一個異常似乎是報告運行期錯誤的最好方法。但事實上你還必須考慮一些額外的因素。你尤其應該對從析構(gòu)函數(shù)中拋出異常保持警惕。從析構(gòu)函數(shù)中拋出異常是危險的從析構(gòu)函數(shù)中拋出異常是不應該被推薦的,這是因為一個析構(gòu)函數(shù)可能會在另一個異常進行stack unwinding的時候被調(diào)用,在這種情況下,異常處理機制就會調(diào)用terminate()終止程序。如果你真的想從一個析構(gòu)函數(shù)中拋出異常的話,一種可取的做法是首先檢查一下是否還有未被捕獲的異常存在。檢查未被捕獲的異常一個異常被捕獲是在它相應的handler被找到的情況下。為了檢查一個異常

40、是否被捕獲,你可以使用標準函數(shù)uncaught_exception()(它被定義在標準頭文件<stdexcept>)。例如:class FileException;File:File() throw (FileException)   if ( close(file_handle) != success) / failed to close current file?      if (uncaught_exception()  = true ) / is there any uncaught exception

41、                                         /being processed currently?      

42、return;  / if so, do not throw an exception    throw FileException(); / otherwise, it is safe to throw an exception                           / to sig

43、nal an error    return; / success然而,一個更好的選擇是直接在析構(gòu)函數(shù)內(nèi)部處理異常,而不是讓他們擴散到外面。例如:void cleanup() throw (int);class C public:  C();C:C()   try       cleanup();    catch(int)       /handle the exception within the destructor  如果一個異常被函數(shù)c

44、leanup()拋出,那么它在析構(gòu)函數(shù)內(nèi)部就被處理。否則,被拋出的異常就會傳播到析構(gòu)函數(shù)的外部,并且如果這個析構(gòu)函數(shù)是在stack unwinding 的過程中被調(diào)用,那么程序?qū)ㄟ^terminate()的調(diào)用而終止。全局對象:構(gòu)造和銷毀我們都知道,全局對象的構(gòu)造發(fā)生在程序開始之前。因此,任何從全局對象的構(gòu)造函數(shù)中拋出的異常將不會被捕獲。這一點對于全局對象的析構(gòu)函數(shù)也是一樣的-全局對象的析構(gòu)函數(shù)在程序結(jié)束之后被運行。因此,一個從全局對象的析構(gòu)函數(shù)中拋出的異常也不會被捕獲。高級異常處理技術(shù)簡單的try-throw-catch模型可以被擴展來處理更為復雜的運行期錯誤。這一節(jié)將會討論一些更為高級的

45、異常處理技術(shù),包括異常層次,重新拋出異常,function try blocks以及auto_ptr 類。標準異常C+定義了一個標準異常層次,當在運行時發(fā)生反常情形時拋出。標準異常類從std:exception(在<stdexcept>頭文件中定義)派生。這一層次使得應用程序能夠在單一的catch語句中捕獲這些異常:catch (std:exception& exc)  / handle exception of type std:exception as well as   /any exception derived from it那些通過語言內(nèi)建操

46、作符拋出的標準異常是:std:bad_alloc     /by operator newstd:bad_cast     /by operator dynamic_cast < >std:bad_typeid   /by operator typeidstd:bad_exception   /thrown when an exception specification of 所有的標準異常都提供了成員函數(shù)what(),它返回一個用來描述異常細節(jié)的字符串。注意,標

47、準庫還有另外一個被它的組件拋出的的異常集合。異常處理層次異常在一個自下向上的層次中捕獲:派生層次越深的異常越先被處理,例如:#include <stdexcept>  #include <iostream>using namespace std;int main()  try        char * buff = new char100000000;      /.use buff    catch(bad_alloc

48、& alloc_failure)   / bad_alloc is                                          /derived fr

49、om exception      cout<<"memory allocation failure"    /. handle exception thrown by operator new    catch(exception& std_ex)       cout<< std_ex.what() <<endl;     catch(.)  / exceptions

50、that are not handled elsewhere are caught here      cout<<"unrecognized exception"<<endl;    return 0;派生層次越深的handler必須出現(xiàn)在其基類的前面。這是因為handler的匹配過程是按照出現(xiàn)的順序進行的。因此有可能某個handler永遠不會被執(zhí)行,例如,把一個處理派生類異常的handler放在處理基類異常的handler的后面。例如:catch(std:exception& s

51、td_ex) /bad_alloc exception is always handled here     /.handle the exception catch(std:bad_alloc& alloc_failure)   /unreachable   cout<<"memory allocation failure"重新拋出異常異常的拋出表明了一種反常的狀態(tài)。先捕獲到異常的handler試圖解決這個問題,但是它如果沒有成功或者只完成了部分恢復,那么它可以重新拋出這個異常,讓更高一層的t

52、ry block來處理它?;谶@種目的,try blocks可以按層次進行嵌套,使得一個從低層重新拋出的異常能夠被重新捕獲。重新拋出用一個沒有操作數(shù)的throw語句來表示。例如:#include <iostream>#include <string>using namespace std;enum SUCCESS, FAILURE;class File  public: File (const char *)   public: bool IsValid() const return false;   public: int OpenNew

53、() const return FAILURE; ;class Exception /*.*/; /general base class for exceptionsclass FileException: public Exception  public: FileException(const char *p) : s(p)   public: const char * Error() const return s.c_str();   private: string s;void func(File& );int main()  try /

54、outer try      File f ("db.dat");    func;   / 1  catch(.) / 7  /this handler will catch the re-thrown exception;   /note: the same exception type is required      cout<<"re-thrown exception caught"    return 0;void func(File & f)  try /i

溫馨提示

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

評論

0/150

提交評論