當(dāng)前位置:首頁 > 芯聞號(hào) > 充電吧
[導(dǎo)讀]一、什么是異常處理 ??????? 一句話:異常處理就是處理程序中的錯(cuò)誤。 二、為什么需要異常處理,以及異常處理的基本思想 ??????? C++之父Bjarne Stroustrup在《The

一、什么是異常處理

??????? 一句話:異常處理就是處理程序中的錯(cuò)誤。

二、為什么需要異常處理,以及異常處理的基本思想

??????? C++之父Bjarne Stroustrup在《The C++ Programming Language》中講到:一個(gè)庫的作者可以檢測(cè)出發(fā)生了運(yùn)行時(shí)錯(cuò)誤,但一般不知道怎樣去處理它們(因?yàn)楹陀脩艟唧w的應(yīng)用有關(guān));另一方面,庫的用戶知道怎樣處理這些錯(cuò)誤,但卻無法檢查它們何時(shí)發(fā)生(如果能檢測(cè),就可以再用戶的代碼里處理了,不用留給庫去發(fā)現(xiàn))。

??????? Bjarne Stroustrup說:提供異常的基本目的就是為了處理上面的問題?;舅枷胧牵鹤屢粋€(gè)函數(shù)在發(fā)現(xiàn)了自己無法處理的錯(cuò)誤時(shí)拋出(throw)一個(gè)異常,然后它的(直接或者間接)調(diào)用者能夠處理這個(gè)問題。?
The fundamental idea is that a function that finds a problem it cannot cope with throws an exception, hoping that its (direct or indirect) caller can handle the problem.

??????? 也就是《C++ primer》中說的:將問題檢測(cè)和問題處理相分離。?
Exceptions let us separate problem detection from problem resolution

??????? 一種思想:在所有支持異常處理的編程語言中(例如java),要認(rèn)識(shí)到的一個(gè)思想:在異常處理過程中,由問題檢測(cè)代碼可以拋出一個(gè)對(duì)象給問題處理代碼,通過這個(gè)對(duì)象的類型和內(nèi)容,實(shí)際上完成了兩個(gè)部分的通信,通信的內(nèi)容是“出現(xiàn)了什么錯(cuò)誤”。當(dāng)然,各種語言對(duì)異常的具體實(shí)現(xiàn)有著或多或少的區(qū)別,但是這個(gè)通信的思想是不變的。

三、異常出現(xiàn)之前處理錯(cuò)誤的方式

??????? 在C語言的世界中,對(duì)錯(cuò)誤的處理總是圍繞著兩種方法:一是使用整型的返回值標(biāo)識(shí)錯(cuò)誤;二是使用errno宏(可以簡(jiǎn)單的理解為一個(gè)全局整型變量)去記錄錯(cuò)誤。當(dāng)然C++中仍然是可以用這兩種方法的。

??????? 這兩種方法最大的缺陷就是會(huì)出現(xiàn)不一致問題。例如有些函數(shù)返回1表示成功,返回0表示出錯(cuò);而有些函數(shù)返回0表示成功,返回非0表示出錯(cuò)。

??????? 還有一個(gè)缺點(diǎn)就是函數(shù)的返回值只有一個(gè),你通過函數(shù)的返回值表示錯(cuò)誤代碼,那么函數(shù)就不能返回其他的值。當(dāng)然,你也可以通過指針或者C++的引用來返回另外的值,但是這樣可能會(huì)令你的程序略微晦澀難懂。

四、異常為什么好

??? 在如果使用異常處理的優(yōu)點(diǎn)有以下幾點(diǎn):

??????? 1. 函數(shù)的返回值可以忽略,但異常不可忽略。如果程序出現(xiàn)異常,但是沒有被捕獲,程序就會(huì)終止,這多少會(huì)促使程序員開發(fā)出來的程序更健壯一點(diǎn)。而如果使用C語言的error宏或者函數(shù)返回值,調(diào)用者都有可能忘記檢查,從而沒有對(duì)錯(cuò)誤進(jìn)行處理,結(jié)果造成程序莫名其面的終止或出現(xiàn)錯(cuò)誤的結(jié)果。

??????? 2. 整型返回值沒有任何語義信息。而異常卻包含語義信息,有時(shí)你從類名就能夠體現(xiàn)出來。

??????? 3. 整型返回值缺乏相關(guān)的上下文信息。異常作為一個(gè)類,可以擁有自己的成員,這些成員就可以傳遞足夠的信息。

??????? 4. 異常處理可以在調(diào)用跳級(jí)。這是一個(gè)代碼編寫時(shí)的問題:假設(shè)在有多個(gè)函數(shù)的調(diào)用棧中出現(xiàn)了某個(gè)錯(cuò)誤,使用整型返回碼要求你在每一級(jí)函數(shù)中都要進(jìn)行處理。而使用異常處理的棧展開機(jī)制,只需要在一處進(jìn)行處理就可以了,不需要每級(jí)函數(shù)都處理。

五、C++中使用異常時(shí)應(yīng)注意的問題

??? 任何事情都是兩面性的,異常有好處就有壞處。如果你是C++程序員,并且希望在你的代碼中使用異常,那么下面的問題是你要注意的。

??????? 1. 性能問題。這個(gè)一般不會(huì)成為瓶頸,但是如果你編寫的是高性能或者實(shí)時(shí)性要求比較強(qiáng)的軟件,就需要考慮了。

(如果你像我一樣,曾經(jīng)是java程序員,那么下面的事情可能會(huì)讓你一時(shí)迷糊,但是沒辦法,誰叫你現(xiàn)在學(xué)的是C++呢。)

???????2. 指針和動(dòng)態(tài)分配導(dǎo)致的內(nèi)存回收問題:在C++中,不會(huì)自動(dòng)回收動(dòng)態(tài)分配的內(nèi)存,如果遇到異常就需要考慮是否正確的回收了內(nèi)存。在java中,就基本不需要考慮這個(gè),有垃圾回收機(jī)制真好!

??????? 3. 函數(shù)的異常拋出列表:java中是如果一個(gè)函數(shù)沒有在異常拋出列表中顯式指定要拋出的異常,就不允許拋出;可是在C++中是如果你沒有在函數(shù)的異常拋出列表指定要拋出的異常,意味著你可以拋出任何異常。

??????? 4. C++中編譯時(shí)不會(huì)檢查函數(shù)的異常拋出列表。這意味著你在編寫C++程序時(shí),如果在函數(shù)中拋出了沒有在異常拋出列表中聲明的異常,編譯時(shí)是不會(huì)報(bào)錯(cuò)的。而在java中,eclipse的提示功能真的好強(qiáng)大啊!

??????? 5. 在java中,拋出的異常都要是一個(gè)異常類;但是在C++中,你可以拋出任何類型,你甚至可以拋出一個(gè)整型。(當(dāng)然,在C++中如果你catch中接收時(shí)使用的是對(duì)象,而不是引用的話,那么你拋出的對(duì)象必須要是能夠復(fù)制的。這是語言的要求,不是異常處理的要求)。

??????? 6. 在C++中是沒有finally關(guān)鍵字的。而java和python中都是有finally關(guān)鍵字的。

六、異常的基本語法

1. 拋出和捕獲異常

??????? 很簡(jiǎn)單,拋出異常用throw,捕獲用try……catch。

??????? 捕獲異常時(shí)的注意事項(xiàng):

???????????? 1. catch子句中的異常說明符必須是完全類型,不可以為前置聲明,因?yàn)槟愕漠惓L幚碇谐3RL問異常類的成員。例外:只有你的catch子句使用指針或者引用接收參數(shù),并且在catch子句內(nèi)你不訪問異常類的成員,那么你的catch子句的異常說明符才可以是前置聲明的類型。

???????????? 2. catch的匹配過程是找最先匹配的,不是最佳匹配。

???????????? 3. catch的匹配過程中,對(duì)類型的要求比較嚴(yán)格。不允許標(biāo)準(zhǔn)算術(shù)轉(zhuǎn)換和類類型的轉(zhuǎn)換。(類類型的轉(zhuǎn)化包括兩種:通過構(gòu)造函數(shù)的隱式類型轉(zhuǎn)化和通過轉(zhuǎn)化操作符的類型轉(zhuǎn)化)。

???????????? 4. 和函數(shù)參數(shù)相同的地方有:?
????????????????????①?如果catch中使用基類對(duì)象接收子類對(duì)象,那么會(huì)造成子類對(duì)象分隔(slice)為父類子對(duì)象(通過調(diào)用父類的復(fù)制構(gòu)造函數(shù));?
????????????????????②?如果catch中使用基類對(duì)象的引用接受子類對(duì)象,那么對(duì)虛成員的訪問時(shí),會(huì)發(fā)生動(dòng)態(tài)綁定,即會(huì)多態(tài)調(diào)用。?
????????????????????③?如果catch中使用基類對(duì)象的指針,那么一定要保證throw語句也要拋出指針類型,并且該指針?biāo)赶虻膶?duì)象,在catch語句執(zhí)行是還存在(通常是動(dòng)態(tài)分配的對(duì)象指針)。

???????????? 5. 和函數(shù)參數(shù)不同的地方有:???
????????????????????①?如果throw中拋出一個(gè)對(duì)象,那么無論是catch中使用什么接收(基類對(duì)象、引用、指針或者子類對(duì)象、引用、指針),在傳遞到catch之前,編譯器都會(huì)另外構(gòu)造一個(gè)對(duì)象的副本。也就是說,如果你以一個(gè)throw語句中拋出一個(gè)對(duì)象類型,在catch處通過也是通過一個(gè)對(duì)象接收,那么該對(duì)象經(jīng)歷了兩次復(fù)制,即調(diào)用了兩次復(fù)制構(gòu)造函數(shù)。一次是在throw時(shí),將“拋出到對(duì)象”復(fù)制到一個(gè)“臨時(shí)對(duì)象”(這一步是必須的),然后是因?yàn)閏atch處使用對(duì)象接收,那么需要再從“臨時(shí)對(duì)象”復(fù)制到“catch的形參變量”中; 如果你在catch中使用“引用”來接收參數(shù),那么不需要第二次復(fù)制,即形參的引用指向臨時(shí)變量。?
??????????????????? ② 該對(duì)象的類型與throw語句中體現(xiàn)的靜態(tài)類型相同。也就是說,如果你在throw語句中拋出一個(gè)指向子類對(duì)象的父類引用,那么會(huì)發(fā)生分割現(xiàn)象,即只有子類對(duì)象中的父類部分會(huì)被拋出,拋出對(duì)象的類型也是父類類型。(從實(shí)現(xiàn)上講,是因?yàn)閺?fù)制到“臨時(shí)對(duì)象”的時(shí)候,使用的是throw語句中類型的(這里是父類的)復(fù)制構(gòu)造函數(shù))。?
??????????????????? ③ 不可以進(jìn)行標(biāo)準(zhǔn)算術(shù)轉(zhuǎn)換和類的自定義轉(zhuǎn)換:在函數(shù)參數(shù)匹配的過程中,可以進(jìn)行很多的類型轉(zhuǎn)換。但是在異常匹配的過程中,轉(zhuǎn)換的規(guī)則要嚴(yán)厲。

??????????????????? ④ 異常處理機(jī)制的匹配過程是尋找最先匹配(first fit),函數(shù)調(diào)用的過程是尋找最佳匹配(best fit)。

2. 異常類型

??????? 上面已經(jīng)提到過,在C++中,你可以拋出任何類型的異常。(哎,竟然可以拋出任何類型,剛看到到這個(gè)的時(shí)候,我半天沒反應(yīng)過來,因?yàn)閖ava中這樣是不行的?。?/p>

???????? 注意:也是上面提到過的,在C++中如果你throw語句中拋出一個(gè)對(duì)象,那么你拋出的對(duì)象必須要是能夠復(fù)制的。因?yàn)橐M(jìn)行復(fù)制副本傳遞,這是語言的要求,不是異常處理的要求。(在上面“和函數(shù)參數(shù)不同的地方”中也講到了,因?yàn)槭且獜?fù)制先到一個(gè)臨時(shí)變量中)

3. 棧展開

??????? 棧展開指的是:當(dāng)異常拋出后,匹配catch的過程。

??????? 拋出異常時(shí),將暫停當(dāng)前函數(shù)的執(zhí)行,開始查找匹配的catch子句。沿著函數(shù)的嵌套調(diào)用鏈向上查找,直到找到一個(gè)匹配的catch子句,或者找不到匹配的catch子句。

??????? 注意事項(xiàng):

?????????????? 1. 在棧展開期間,會(huì)銷毀局部對(duì)象。

???????????????????? ① 如果局部對(duì)象是類對(duì)象,那么通過調(diào)用它的析構(gòu)函數(shù)銷毀。

???????????????????? ② 但是對(duì)于通過動(dòng)態(tài)分配得到的對(duì)象,編譯器不會(huì)自動(dòng)刪除,所以我們必須手動(dòng)顯式刪除。(這個(gè)問題是如此的常見和重要,以至于會(huì)用到一種叫做RAII的方法,詳情見下面講述)

?????????????? 2.?析構(gòu)函數(shù)應(yīng)該從不拋出異常。如果析構(gòu)函數(shù)中需要執(zhí)行可能會(huì)拋出異常的代碼,那么就應(yīng)該在析構(gòu)函數(shù)內(nèi)部將這個(gè)異常進(jìn)行處理,而不是將異常拋出去。

???????????????????? 原因:在為某個(gè)異常進(jìn)行棧展開時(shí),析構(gòu)函數(shù)如果又拋出自己的未經(jīng)處理的另一個(gè)異常,將會(huì)導(dǎo)致調(diào)用標(biāo)準(zhǔn)庫 terminate 函數(shù)。而默認(rèn)的terminate 函數(shù)將調(diào)用 abort 函數(shù),強(qiáng)制從整個(gè)程序非正常退出。

?????????????? 3.?構(gòu)造函數(shù)中可以拋出異常。但是要注意到:如果構(gòu)造函數(shù)因?yàn)楫惓6顺?,那么該類的析?gòu)函數(shù)就得不到執(zhí)行。所以要手動(dòng)銷毀在異常拋出前已經(jīng)構(gòu)造的部分。

4. 異常重新拋出

??????? 語法:使用一個(gè)空的throw語句。即寫成: throw;???

??????? 注意問題:

??????????????? ① throw;? 語句出現(xiàn)的位置,只能是catch子句中或者是catch子句調(diào)用的函數(shù)中。?
??????????????? ② 重新拋出的是原來的異常對(duì)象,即上面提到的“臨時(shí)變量”,不是catch形參。?
??????????????? ③ 如果希望在重新拋出之前修改異常對(duì)象,那么應(yīng)該在catch中使用引用參數(shù)。如果使用對(duì)象接收的話,那么修改異常對(duì)象以后,不能通過“重新拋出”來傳播修改的異常對(duì)象,因?yàn)橹匦聮伋霾皇莄atch形參,應(yīng)該使用的是 throw e;? 這里“e”為catch語句中接收的對(duì)象參數(shù)。

5. 捕獲所有異常(匹配任何異常)

??????? 語法:在catch語句中,使用三個(gè)點(diǎn)(…)。即寫成:catch (…)?? 這里三個(gè)點(diǎn)是“通配符”,類似 可變長(zhǎng)形式參數(shù)。

??????? 常見用法:與“重新拋出”表達(dá)式一起使用,在catch中完成部分工作,然后重新拋出異常。

6. 未捕獲的異常

??????? 意思是說,如果程序中有拋出異常的地方,那么就一定要對(duì)其進(jìn)行捕獲處理。否則,如果程序執(zhí)行過程中拋出了一個(gè)異常,而又沒有找到相應(yīng)的catch語句,那么會(huì)和“棧展開過程中析構(gòu)函數(shù)拋出異常”一樣,會(huì) 調(diào)用terminate 函數(shù),而默認(rèn)的terminate 函數(shù)將調(diào)用 abort 函數(shù),強(qiáng)制從整個(gè)程序非正常退出。

7. 構(gòu)造函數(shù)的函數(shù)測(cè)試塊

??????? 對(duì)于在構(gòu)造函數(shù)的初始化列表中拋出的異常,必須使用函數(shù)測(cè)試塊(function try block)來進(jìn)行捕捉。語法類型下面的形式:

[cpp]?view plaincopyprint? MyClass::MyClass(int?i)?? try?:member(i)?{?? ????//函數(shù)體?? }?catch(異常參數(shù))?{?? ????//異常處理代碼?? }??

??????? 注意事項(xiàng):在函數(shù)測(cè)試塊中捕獲的異常,在catch語句中可以執(zhí)行一個(gè)內(nèi)存釋放操作,然后異常仍然會(huì)再次拋出到用戶代碼中。

8. 異常拋出列表(異常說明 exception specification)

??????? 就是在函數(shù)的形參表之后(如果是const成員函數(shù),那么在const之后),使用關(guān)鍵字throw聲明一個(gè)帶著括號(hào)的、可能為空的 異常類型列表。形如:throw ()? 或者 throw (runtime_error, bad_alloc)?? 。

??????? 含義:表示該函數(shù)只能拋出?在列表中的異常類型。例如:throw() 表示不拋出任何異常。而throw (runtime_error, bad_alloc)表示只能拋出runtime_error 或bad_alloc兩種異常。

??????? 注意事項(xiàng):(以前學(xué)java的尤其要注意,和java中不太一樣)

??????????????? ① 如果函數(shù)沒有顯式的聲明 拋出列表,表示異??梢話伋鋈我饬斜?。(在java中,如果沒有異常拋出列表,那么是不能拋出任何異常的)。

??????????????? ② C++的 “throw()”相當(dāng)于java的不聲明拋出列表。都表示不拋出任何異常。

??????????????? ③ 在C++中,編譯的時(shí)候,編譯器不會(huì)對(duì)異常拋出列表進(jìn)行檢查。也就是說,如果你聲明了拋出列表,即使你的函數(shù)代碼中拋出了沒有在拋出列表中指定的異常,你的程序依然可以通過編譯,到運(yùn)行時(shí)才會(huì)出錯(cuò),對(duì)于這樣的異常,在C++中稱為“意外異?!保╱nexpeced exception)。(這點(diǎn)和java又不相同,在java中,是要進(jìn)行嚴(yán)格的檢查的)。?

????????意外異常的處理:?
??????????????? 如果程序中出現(xiàn)了意外異常,那么程序就會(huì)調(diào)用函數(shù)unexpected()。這個(gè)函數(shù)的默認(rèn)實(shí)現(xiàn)是調(diào)用terminate函數(shù),即默認(rèn)最終會(huì)終止程序。

????????虛函數(shù)重載方法時(shí)異常拋出列表的限制:?
??????????????? 在子類中重載時(shí),函數(shù)的異常說明 必須要比父類中要同樣嚴(yán)格,或者更嚴(yán)格。換句話說,在子類中相應(yīng)函數(shù)的異常說明不能增加新的異常?;蛘咴贀Q句話說:父類中異常拋出列表是該虛函數(shù)的子類重載版本可以拋出異常列表的?超集。

????????函數(shù)指針中異常拋出列表的限制:?
???????????????? 異常拋出列表是函數(shù)類型的一部分,在函數(shù)指針中也可以指定異常拋出列表。但是在函數(shù)指針初始化或者賦值時(shí),除了要檢查返回值和形式參數(shù)外,還要注意異常拋出列表的限制:源指針的異常說明必須至少和目標(biāo)指針的一樣嚴(yán)格。比較拗口,換句話說,就是聲明函數(shù)指針時(shí)指定的異常拋出列表,一定要實(shí)際函數(shù)的異常拋出列表的超集。 如果定義函數(shù)指針時(shí)不提供異常拋出列表,那么可以指向能夠拋出任意類型異常的函數(shù)。????????????????

????????拋出列表是否有用???
???????????????? 在《More effective C++》第14條,Scott Meyers指出“要謹(jǐn)慎的使用異常說明”(Use exception specifications judiciously)。“異常說明”,就是我們所有的“異常拋出列表”。之所以要謹(jǐn)慎,根本原因是因?yàn)镃++編譯器不會(huì)檢查異常拋出列表,這樣就可能在函數(shù)代碼中、或者調(diào)用的函數(shù)中拋出了沒有在拋出列表中指定的異常,從而導(dǎo)致程序調(diào)用unexpected函數(shù),造成程序提前終止。同時(shí)他給出了三條要考慮的事情:?
???????????????????????? ① 在模板中不要使用異常拋出列表。(原因很簡(jiǎn)單,連用來實(shí)例模板的類型都不知道,也就無法確定該函數(shù)是否應(yīng)該拋出異常,拋出什么異常)。??
???????????????????????? ② 如果A函數(shù)內(nèi)調(diào)用了B函數(shù),而B函數(shù)沒有聲明異常拋出列表,那么A函數(shù)本身也不應(yīng)該設(shè)定異常拋出列表。(原因是,B函數(shù)可能拋出沒有在A函數(shù)的異常拋出列表中聲明的異常,會(huì)導(dǎo)致調(diào)用unex函數(shù));?
???????????????????????? ③ 通過set_unexpected函數(shù)指定一個(gè)新的unexpected函數(shù),在該函數(shù)中捕獲異常,并拋出一個(gè)統(tǒng)一類型的異常。

???????????????? 另外,在《C++ Primer》4th 中指出,雖然異常說明應(yīng)用有限,但是如果能夠確定該函數(shù)不會(huì)拋出異常,那么顯式聲明其不拋出任何異常 有好處。通過語句:"throw ()"。這樣的好處是:對(duì)于程序員,當(dāng)調(diào)用這樣的函數(shù)時(shí),不需要擔(dān)心異常。對(duì)于編譯器,可以執(zhí)行被可能拋出異常所抑制的優(yōu)化。

七、標(biāo)準(zhǔn)庫中的異常類

??????? 和java一樣,標(biāo)準(zhǔn)庫中也提供了很多的異常類,它們是通過類繼承組織起來的。標(biāo)準(zhǔn)異常被組織成八個(gè)。

????????異常類繼承層級(jí)結(jié)構(gòu)圖如下:?

??? 每個(gè)類所在的頭文件在圖下方標(biāo)識(shí)出來.

????標(biāo)準(zhǔn)異常類的成員:?
??????? ① 在上述繼承體系中,每個(gè)類都有提供了構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、和賦值操作符重載。?
??????? ② logic_error類及其子類、runtime_error類及其子類,它們的構(gòu)造函數(shù)是接受一個(gè)string類型的形式參數(shù),用于異常信息的描述;?
??????? ③ 所有的異常類都有一個(gè)what()方法,返回const char* 類型(C風(fēng)格字符串)的值,描述異常信息。

????標(biāo)準(zhǔn)異常類的具體描述:?

異常名稱

描述

exception 所有標(biāo)準(zhǔn)異常類的父類 bad_alloc 當(dāng)operator new and operator new[],請(qǐng)求分配內(nèi)存失敗時(shí) bad_exception 這是個(gè)特殊的異常,如果函數(shù)的異常拋出列表里聲明了bad_exception異常,當(dāng)函數(shù)內(nèi)部拋出了異常拋出列表中沒有的異常,這是調(diào)用的unexpected函數(shù)中若拋出異常,不論什么類型,都會(huì)被替換為bad_exception類型 bad_typeid 使用typeid操作符,操作一個(gè)NULL指針,而該指針是帶有虛函數(shù)的類,這時(shí)拋出bad_typeid異常 bad_cast 使用dynamic_cast轉(zhuǎn)換引用失敗的時(shí)候 ios_base::failure io操作過程出現(xiàn)錯(cuò)誤 logic_error 邏輯錯(cuò)誤,可以在運(yùn)行前檢測(cè)的錯(cuò)誤 runtime_error 運(yùn)行時(shí)錯(cuò)誤,僅在運(yùn)行時(shí)才可以檢測(cè)的錯(cuò)誤

??????? logic_error的子類:?

異常名稱

描述

length_error 試圖生成一個(gè)超出該類型最大長(zhǎng)度的對(duì)象時(shí),例如vector的resize操作 domain_error 參數(shù)的值域錯(cuò)誤,主要用在數(shù)學(xué)函數(shù)中。例如使用一個(gè)負(fù)值調(diào)用只能操作非負(fù)數(shù)的函數(shù) out_of_range 超出有效范圍 invalid_argument 參數(shù)不合適。在標(biāo)準(zhǔn)庫中,當(dāng)利用string對(duì)象構(gòu)造bitset時(shí),而string中的字符不是’0’或’1’的時(shí)候,拋出該異常

??????? runtime_error的子類:?

異常名稱

描述

range_error 計(jì)算結(jié)果超出了有意義的值域范圍 overflow_error 算術(shù)計(jì)算上溢 underflow_error 算術(shù)計(jì)算下溢

八、編寫自己的異常類

????????1. 為什么要編寫自己的異常類??
??????????????? ① 標(biāo)準(zhǔn)庫中的異常是有限的;?
??????????????? ② 在自己的異常類中,可以添加自己的信息。(標(biāo)準(zhǔn)庫中的異常類值允許設(shè)置一個(gè)用來描述異常的字符串)。

????????2. 如何編寫自己的異常類??
??????????????? ①?建議自己的異常類要繼承標(biāo)準(zhǔn)異常類。因?yàn)镃++中可以拋出任何類型的異常,所以我們的異常類可以不繼承自標(biāo)準(zhǔn)異常,但是這樣可能會(huì)導(dǎo)致程序混亂,尤其是當(dāng)我們多人協(xié)同開發(fā)時(shí)。?
??????????????? ② 當(dāng)繼承標(biāo)準(zhǔn)異常類時(shí),應(yīng)該重載父類的what函數(shù)和虛析構(gòu)函數(shù)。?
??????????????? ③ 因?yàn)闂U归_的過程中,要復(fù)制異常類型,那么要根據(jù)你在類中添加的成員考慮是否提供自己的復(fù)制構(gòu)造函數(shù)。

九、用類來封裝資源分配和釋放

????????為什么要使用類來封裝資源分配和釋放???
??????????????? 為了防止內(nèi)存泄露。因?yàn)樵诤瘮?shù)中發(fā)生異常,那么對(duì)于動(dòng)態(tài)分配的資源,就不會(huì)自動(dòng)釋放,必須要手動(dòng)顯式釋放,否則就會(huì)內(nèi)存泄露。而對(duì)于類對(duì)象,會(huì)自動(dòng)調(diào)用其析構(gòu)函數(shù)。如果我們?cè)谖鰳?gòu)函數(shù)中顯式delete這些資源,就能保證這些動(dòng)態(tài)分配的資源會(huì)被釋放。

????????如何編寫這樣的類???
??????????????? 將資源的分配和銷毀用類封轉(zhuǎn)起來。在析構(gòu)函數(shù)中要顯式的釋放(delete或delete[])這些資源。這樣,若用戶代碼中發(fā)生異常,當(dāng)作用域結(jié)束時(shí),會(huì)調(diào)用給該類的析構(gòu)函數(shù)釋放資源。這種技術(shù)被稱為:資源分配即初始化。(resource allocation is initialization,縮寫為"RAII")。

十、auto_ptr的使用(非常重要)

??????? “用類封裝資源的分配和釋放”是如此的重要,C++標(biāo)準(zhǔn)庫為我們提供了一個(gè)模板類來實(shí)現(xiàn)這個(gè)功能。名稱為auto_ptr,在memory頭文件中。

????????auto_ptr類的成員如下:(摘自《C++ Primer》)?

函數(shù)

功能

auto_ptr ap() 默認(rèn)構(gòu)造函數(shù),創(chuàng)建名為ap的未綁定的auto_ptr對(duì)象 auto_ptr ap(p); 創(chuàng)建名為 ap 的 auto_ptr 對(duì)象,ap 擁有指針 p 指向的對(duì)象。該構(gòu)造函數(shù)為?explicit auto_ptr ap1(ap2); 創(chuàng)建名為 ap1 的 auto_ptr 對(duì)象,ap1 保存原來存儲(chǔ)在 ap2 中的指針。將所有權(quán)轉(zhuǎn)給 ap1,ap2 成為未綁定的 auto_ptr 對(duì)象 ap1 = ap2 將所有權(quán) ap2 轉(zhuǎn)給 ap1。刪除 ap1 指向的對(duì)象并且使 ap1 指向 ap2 指向的對(duì)象,使 ap2 成為未綁定的 ~ap 析構(gòu)函數(shù)。刪除 ap 指向的對(duì)象 *ap 返回對(duì) ap 所綁定的對(duì)象的引用 ap-> 返回 ap 保存的指針 ap.reset(p) 如果 p 與 ap 的值不同,則刪除 ap 指向的對(duì)象并且將 ap 綁定到 p ap.release() 返回 ap 所保存的指針并且使 ap 成為未綁定的 ap.get() 返回 ap 保存的指針

????????auto_ptr類的使用:?
??????????????? 1. 用來保存一個(gè)指向?qū)ο箢愋偷闹羔?。注意必須是?dòng)態(tài)分配的對(duì)象(即使用new非配的)的指針。既不能是動(dòng)態(tài)分配的數(shù)組(使用new [])指針,也不能是非動(dòng)態(tài)分配的對(duì)象指針。?
??????????????? 2. 慣用的初始化方法:在用戶代碼中,使用new表達(dá)式作為auto_ptr構(gòu)造函數(shù)的參數(shù)。(注意:auto_ptr類接受指針參數(shù)的構(gòu)造函數(shù)為explicit,所以必須顯式的進(jìn)行初始化)。?
??????????????? 3. auto_ptr的行為特征:類似普通指針行為。auto_ptr存在的主要原因就是,為了防止動(dòng)態(tài)分配的對(duì)象指針造成的內(nèi)存泄露,既然是指針,其具有"*"操作符和"->"操作符。所以auto_ptr的主要目的就是:首先保證自動(dòng)刪除auto_ptr所引用的對(duì)象,并且要支持普通指針行為。?
??????????????? 4. auto_ptr對(duì)象的復(fù)制和賦值是有破壞性的。① 會(huì)導(dǎo)致右操作數(shù)成為未綁定的,導(dǎo)致auto_ptr對(duì)象不能放到容器中;② 在賦值的時(shí)候,將有操作符修改為未綁定,即修改了右操作數(shù),所以要保證這里的賦值操作符右操作數(shù)是可以修改的左值(然而普通的賦值操作符中,右操作數(shù)可以不是左值);③和普通的賦值操作符一樣,如果是自我賦值,那么沒有效果;④ 導(dǎo)致auto_ptr對(duì)象不能放到容器中。?
??????????????? 5. 如果auto_ptr初始化的時(shí)候,使用默認(rèn)構(gòu)造函數(shù),成為未綁定的auto_ptr對(duì)象,那么可以通過reset操作將其綁定到一個(gè)對(duì)象。?
??????????????? 6. 如果希望測(cè)試auto_ptr是否已經(jīng)綁定到了一個(gè)對(duì)象,那么使用get()函數(shù)的返回值與NULL進(jìn)行比較。

???????auto_ptr的缺陷:?
??????????????? 1. 不能使用auto_ptr對(duì)象保存指向靜態(tài)分配的對(duì)象的指針,也不能保存指向動(dòng)態(tài)分配的數(shù)組的指針。?
??????????????? 2. 不能講兩個(gè)auto_ptr對(duì)象指向同一個(gè)對(duì)象。因?yàn)樵谝粋€(gè)auto_ptr對(duì)象析構(gòu)以后,造成另一個(gè)auto_ptr對(duì)象指向了已經(jīng)釋放的內(nèi)存。造成這種情況的兩種主要常見原因是:① 用同一個(gè)指針來初始化或者reset兩個(gè)不同的auto_ptr對(duì)象;② 使用一個(gè)auto_ptr對(duì)象的get函數(shù)返回值去初始化或者reset另一個(gè)auto_ptr對(duì)象。?
??????????????? 3.?不能將auto_ptr對(duì)象放到容器中。因?yàn)槠鋸?fù)制和賦值操作具有破壞性。

十一、常見的異常處理問題

??? 動(dòng)態(tài)內(nèi)存分配錯(cuò)誤

???????? ① 分配動(dòng)態(tài)內(nèi)存使用的是new和new[]操作符,如果他們分配內(nèi)存失敗,就會(huì)拋出bad_alloc異常,在new頭文件中,所以我們的代碼中應(yīng)該捕捉這些異常。常見的代碼形式如下:

[cpp]?view plaincopyprint? try?{?? ????//其他代碼?? ????ptr?=?new?int[num_max];?? ????//其他代碼?? }?catch(bad_alloc?&e)?{?? ????//這里常見的處理方式為:先釋放已經(jīng)分配的內(nèi)存,然后結(jié)束程序,或者打印一條錯(cuò)誤信息并繼續(xù)執(zhí)行?? }??

???????? ② 可以使用類似C語言的方式處理,但這時(shí)要使用的nothrow版本,使用"new (nothrow)"的形式分配內(nèi)存。這時(shí),如果分配不成功,返回的是NULL指針,而不再是拋出bad_alloc異常。?
???????? ③ 可以定制內(nèi)存分配失敗行為。C++允許指定一個(gè)new 處理程序(newhandler)回調(diào)函數(shù)。默認(rèn)的并沒有new 處理程序,如果我們?cè)O(shè)置了new 處理程序,那么當(dāng)new和new[] 分配內(nèi)存失敗時(shí),會(huì)調(diào)用我們?cè)O(shè)定的new 處理程序,而不是直接拋出異常。通過set_new_handler函數(shù)來設(shè)置該回調(diào)函數(shù)。要求被回調(diào)的函數(shù)沒有返回值,也沒有形式參數(shù)。

十二、來自C++之父Bjarne Stroustrup的建議

??? 節(jié)選自《The C++ Programming Language》 ——C++之父Bjarne Stroustrup?
???????? 1.?Don’t use exceptions where more local control structures will suffice;??? 當(dāng)局部的控制能夠處理時(shí),不要使用異常;?
???????? 2.?Use the "resource allocation is initialization" technique to manage resources;?? 使用“資源分配即初始化”技術(shù)去管理資源;?
???????? 3.?Minimize the use of try-blocks. Use "resource acquisition is initialization" instead of explicit handler code;??? 盡量少用try-catch語句塊,而是使用“資源分配即初始化”技術(shù)。?
???????? 4.?Throw an exception to indicate failure in a constructor;???? 如果構(gòu)造函數(shù)內(nèi)發(fā)生錯(cuò)誤,通過拋出異常來指明。?
???????? 5.?Avoid throwing exceptions from destructors;???? 避免在析構(gòu)函數(shù)中拋出異常。?
???????? 6.?Keep ordinary code and error-handling code separate;????? 保持普通程序代碼和異常處理代碼分開。?
???????? 7.?Beware of memory leaks caused by memory allocated by?new?not being released in case of an exception;? 小心通過new分配的內(nèi)存在發(fā)生異常時(shí),可能造成內(nèi)存泄露。?
???????? 8.?Assume that every exception that can be thrown by a function will be thrown;??? 如果一個(gè)函數(shù)可能拋出某種異常,那么我們調(diào)用它時(shí),就要假定它一定會(huì)拋出該異常,即要進(jìn)行處理。?
???????? 9.?Don't assume that every exception is derived from class?exception;???? 要記住,不是所有的異常都繼承自exception類。?
???????? 10.?A library shouldn't unilaterally terminate a program. Instead, throw an exception and let a caller decide;??? 編寫的供別人調(diào)用的程序庫,不應(yīng)該結(jié)束程序,而應(yīng)該通過拋出異常,讓調(diào)用者決定如何處理(因?yàn)檎{(diào)用者必須要處理拋出的異常)。?
???????? 11.?Develop an error-handling strategy early in a design;??? 若開發(fā)一個(gè)項(xiàng)目,那么在設(shè)計(jì)階段就要確定“錯(cuò)誤處理的策略”。??

本文鏈接:?http://blog.csdn.net/daheiantian/archive/2011/03/10/6530318.aspx

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉