C++/C++11中std::exception的使用
std::exception:標(biāo)準(zhǔn)異常類的基類,其類的聲明在頭文件<exception>中。所有標(biāo)準(zhǔn)庫(kù)的異常類均繼承于此類,因此通過(guò)引用類型可以捕獲所有標(biāo)準(zhǔn)異常。
std::exception類定義了無(wú)參構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、拷貝賦值運(yùn)算符、一個(gè)虛析構(gòu)函數(shù)和一個(gè)名為what的無(wú)參虛成員。其中what函數(shù)返回一個(gè)const char*,該指針指向一個(gè)以null結(jié)尾的字符數(shù)組,并且確保不會(huì)拋出任何異常,該字符串的目的是提供關(guān)于異常的一些文本信息。除析構(gòu)函數(shù)外,其它函數(shù)均通過(guò)關(guān)鍵字noexcept說(shuō)明此函數(shù)不會(huì)拋出異常。
std::exception is the class whose only purpose is to serve as the base class in the exception hierarchy. It has no other uses. In other words, conceptually it is an abstract class (even though it is not defined as abstract class in C++ meaning of the term).
以下內(nèi)容摘自:《C++Primer(Fifth Edition)》
異常是指在程序運(yùn)行時(shí)發(fā)生的反常行為,這些行為超出了函數(shù)正常功能的范圍。典型的異常包括失去數(shù)據(jù)庫(kù)連接以及遇到意外輸入等。當(dāng)程序的某部分檢測(cè)到一個(gè)它無(wú)法處理的問(wèn)題時(shí),需要用到異常處理。此時(shí),檢測(cè)出問(wèn)題的部分應(yīng)該發(fā)出某種信號(hào)以表明程序遇到了故障,無(wú)法繼續(xù)下去了,而且信號(hào)的發(fā)出方無(wú)須知道故障將在何處得到解決。一旦發(fā)出異常信號(hào),檢測(cè)出問(wèn)題的部分也就完成了任務(wù)。
異常提供了一種轉(zhuǎn)移程序控制權(quán)的方式。C++異常處理涉及到三個(gè)關(guān)鍵字:try、catch、throw。關(guān)于這三個(gè)關(guān)鍵字的簡(jiǎn)單使用可以參考: http://blog.csdn.net/fengbingchun/article/details/65939258?
異常處理機(jī)制為程序中異常檢測(cè)和異常處理這兩部分的協(xié)作提供支持。在C++語(yǔ)言中,異常處理包括:
?(1)、throw表達(dá)式(throw expression):異常檢測(cè)部分使用throw表達(dá)式來(lái)表示它遇到了無(wú)法處理的問(wèn)題。throw引發(fā)(raise)異常。throw表達(dá)式包含關(guān)鍵字throw和緊隨其后的一個(gè)表達(dá)式,其中表達(dá)式的類型就是拋出的異常類型。throw表達(dá)式后面通常緊跟一個(gè)分號(hào),從而構(gòu)成一條表達(dá)式語(yǔ)句。拋出異常將終止當(dāng)前的函數(shù),并把控制權(quán)轉(zhuǎn)移給能處理該異常的代碼。
(2)、try語(yǔ)句塊(try block):異常處理部分使用try語(yǔ)句塊處理異常。try語(yǔ)句塊以關(guān)鍵字try開(kāi)始,并以一個(gè)或多個(gè)catch子句(catch clause)結(jié)束。try語(yǔ)句塊中代碼拋出的異常通常會(huì)被某個(gè)catch子句處理。因?yàn)閏atch子句處理異常,所以它們也被稱作異常處理代碼(exception handler)。catch子句包括三部分:關(guān)鍵字catch、括號(hào)內(nèi)一個(gè)(可能未命名的)對(duì)象的聲明(稱作異常聲明,exception declaration)以及一個(gè)塊。當(dāng)選中了某個(gè)catch子句處理異常之后,執(zhí)行與之對(duì)應(yīng)的塊。catch一旦完成,程序跳轉(zhuǎn)到try語(yǔ)句塊最后一個(gè)catch子句之后的那條語(yǔ)句繼續(xù)執(zhí)行。一如往常,try語(yǔ)句塊聲明的變量在塊外部無(wú)法訪問(wèn),特別是在catch子句內(nèi)也無(wú)法訪問(wèn)。如果一段程序沒(méi)有try語(yǔ)句塊且發(fā)生了異常,系統(tǒng)會(huì)調(diào)用terminate函數(shù)并終止當(dāng)前程序的執(zhí)行。
(3)、一套異常類(exception class):用于在throw表達(dá)式和相關(guān)的catch子句之間傳遞異常的具體信息。
函數(shù)在尋找處理代碼的過(guò)程中退出:尋找處理代碼的過(guò)程與函數(shù)調(diào)用鏈剛好相反。當(dāng)異常被拋出時(shí),首先搜索拋出該異常的函數(shù)。如果沒(méi)有找到匹配的catch子句,終止該函數(shù),并在調(diào)用該函數(shù)的函數(shù)中繼續(xù)尋找。如果還是沒(méi)有找到匹配的catch子句,這個(gè)新的函數(shù)也被終止,繼續(xù)搜索調(diào)用它的函數(shù)。以此類推,沿著程序的執(zhí)行路徑逐層回退,直到找到適當(dāng)類型的catch子句為止。如果最終還是沒(méi)能找到任何匹配的catch子句,程序轉(zhuǎn)到名為terminate的標(biāo)準(zhǔn)庫(kù)函數(shù)。該函數(shù)的行為與系統(tǒng)有關(guān),一般情況下,執(zhí)行該函數(shù)將導(dǎo)致程序非正常退出。
如果一段程序沒(méi)有try語(yǔ)句塊且發(fā)生了異常,系統(tǒng)會(huì)調(diào)用terminate函數(shù)并終止當(dāng)前程序的執(zhí)行。
那些在異常發(fā)生期間正確執(zhí)行了”清理”工作的程序被稱作異常安全(exception safe)的代碼。編寫異常安全的代碼非常困難。
標(biāo)準(zhǔn)異常:C++標(biāo)準(zhǔn)庫(kù)定義了一組類,用于報(bào)告標(biāo)準(zhǔn)庫(kù)函數(shù)遇到的問(wèn)題。這些異常類也可以在用戶編寫的程序中使用,它們分別定義在4個(gè)頭文件中:
(1)、exception頭文件定義了最通常的異常類std::exception,它只報(bào)告異常的發(fā)生,不提供任何額外的信息。
(2)、stdexcept頭文件定義了幾種常用的異常類,如下:
(3)、new頭文件定義了bad_alloc異常類型。
(4)、type_info頭文件定義了bad_cast異常類型。
標(biāo)準(zhǔn)庫(kù)異常類只定義了幾種運(yùn)算,包括創(chuàng)建或拷貝異常類型的對(duì)象,以及為異常類型的對(duì)象賦值。我們只能以默認(rèn)初始化的方式初始化exception、bad_alloc和bad_cast對(duì)象,不允許為這些對(duì)象提供初始值。其它異常類型的行為則恰恰相反:應(yīng)該使用string對(duì)象或者C風(fēng)格字符串初始化這些類型的對(duì)象,但是不允許使用默認(rèn)初始化的方式。當(dāng)創(chuàng)建此類對(duì)象時(shí),必須提供初始值,該初始值含有錯(cuò)誤相關(guān)的信息。
異常類型只定義了一個(gè)名為what的成員函數(shù),該函數(shù)沒(méi)有任何參數(shù),返回值是一個(gè)指向C風(fēng)格字符串的const char*。該字符串的目的是提供關(guān)于異常的一些文本信息。what函數(shù)返回的C風(fēng)格字符串的內(nèi)容與異常對(duì)象的類型有關(guān)。如果異常類型有一個(gè)字符串初始值,則what返回該字符串。對(duì)于其它無(wú)初始值的異常類型來(lái)說(shuō),what返回的內(nèi)容由編譯器決定。
異常處理(exception handling)機(jī)制允許程序中獨(dú)立開(kāi)發(fā)的部分能夠在運(yùn)行時(shí)就出現(xiàn)的問(wèn)題進(jìn)行通信并做出相應(yīng)的處理。異常使得我們能夠?qū)?wèn)題的檢測(cè)與解決過(guò)程分離開(kāi)來(lái)。程序的一部分負(fù)責(zé)檢測(cè)問(wèn)題的出現(xiàn),然后解決該問(wèn)題的任務(wù)傳遞給程序中的另一部分。檢測(cè)環(huán)節(jié)無(wú)須知道問(wèn)題處理模塊的所有細(xì)節(jié),反之亦然。
拋出異常:在C++語(yǔ)言中,我們通過(guò)拋出(throwing)一條表達(dá)式來(lái)引發(fā)(raised)一個(gè)異常。被拋出的表達(dá)式的類型以及當(dāng)前的調(diào)用鏈共同決定了哪段處理代碼(handler)將被用來(lái)處理該異常。被選中的處理代碼是在調(diào)用鏈中與拋出對(duì)象類型匹配的最近的處理的代碼。其中,根據(jù)拋出對(duì)象的類型和內(nèi)容,程序的異常拋出部分將會(huì)告知異常處理部分到底發(fā)生了什么錯(cuò)誤。
當(dāng)執(zhí)行一個(gè)throw時(shí),跟在throw后面的語(yǔ)句將不再被執(zhí)行,throw語(yǔ)句的用法有點(diǎn)類似于return語(yǔ)句:它通常作為調(diào)節(jié)語(yǔ)句的一部分或者作為某個(gè)函數(shù)的最后(或者唯一)一條語(yǔ)句。相反,程序的控制權(quán)從throw轉(zhuǎn)移到與之匹配的catch模塊。該catch可能是同一個(gè)函數(shù)中的局部catch,也可能位于直接或間接調(diào)用了發(fā)生異常的函數(shù)的另一個(gè)函數(shù)中。
當(dāng)拋出一個(gè)異常后,程序暫停當(dāng)前函數(shù)的執(zhí)行過(guò)程并立即開(kāi)始尋找與異常匹配的catch子句。當(dāng)throw出現(xiàn)在一個(gè)try語(yǔ)句塊內(nèi)時(shí),檢查與該try塊關(guān)聯(lián)的catch子句。如果找到了匹配的catch,就使用該catch處理異常。如果這一步?jīng)]找到匹配的catch且該try語(yǔ)句嵌套在其它try塊中,則繼續(xù)檢查與外層try匹配的catch子句。如果還是找不到匹配的catch,則退出當(dāng)前的函數(shù),在調(diào)用當(dāng)前函數(shù)的外層函數(shù)中繼續(xù)尋找,依次類推。這一過(guò)程被稱為棧展開(kāi)(stack unwinding)過(guò)程。棧展開(kāi)過(guò)程沿著嵌套函數(shù)的調(diào)用鏈不斷查找,直到找到了與異常匹配的catch子句為止:或者也可能一直沒(méi)找到匹配的catch,則退出主函數(shù)后查找過(guò)程終止。
假設(shè)找到了一個(gè)匹配的catch子句,則程序進(jìn)入該子句并執(zhí)行其中的代碼。當(dāng)執(zhí)行完這個(gè)catch子句后,找到與try塊關(guān)聯(lián)的最后一個(gè)catch子句之后的點(diǎn),并從這里繼續(xù)執(zhí)行。如果沒(méi)找到匹配的catch子句,程序?qū)⑼顺觥R驗(yàn)楫惓Mǔ1徽J(rèn)為是妨礙程序正常執(zhí)行的事件,所以一旦引發(fā)了某個(gè)異常,就不能對(duì)它置之不理。當(dāng)找不到匹配的catch時(shí),程序?qū)⒄{(diào)用標(biāo)準(zhǔn)庫(kù)函數(shù)terminate,terminate負(fù)責(zé)終止程序的執(zhí)行過(guò)程。
Note:一個(gè)異常如果沒(méi)有被捕獲,則它將終止當(dāng)前的程序。
棧展開(kāi)過(guò)程中對(duì)象被自動(dòng)銷毀:在棧展開(kāi)過(guò)程中,位于調(diào)用鏈上的語(yǔ)句塊可能會(huì)提前退出。通常情況下,程序在這些塊中創(chuàng)建了一些局部對(duì)象。塊退出后它的局部對(duì)象也將隨之銷毀,這條規(guī)則對(duì)于棧展開(kāi)過(guò)程同樣適用。如果在棧展開(kāi)過(guò)程中退出了某個(gè)塊,編譯器將負(fù)責(zé)確保在這個(gè)塊中創(chuàng)建的對(duì)象能被正確地銷毀。如果某個(gè)局部對(duì)象的類型是類類型,則該對(duì)象的析構(gòu)函數(shù)將被自動(dòng)調(diào)用。與往常一樣,編譯器在銷毀內(nèi)置類型的對(duì)象時(shí)不需要做任何事情。
析構(gòu)函數(shù)與異常:析構(gòu)函數(shù)總是會(huì)被執(zhí)行的。出于棧展開(kāi)可能使用析構(gòu)函數(shù)的考慮,析構(gòu)函數(shù)不應(yīng)該拋出不能被它自身處理的異常。換句話說(shuō),如果析構(gòu)函數(shù)需要執(zhí)行某個(gè)可能拋出異常的操作,則該操作應(yīng)該被放置在一個(gè)try語(yǔ)句塊當(dāng)中,并且在析構(gòu)函數(shù)內(nèi)部得到處理。一旦在棧展開(kāi)的過(guò)程中析構(gòu)函數(shù)拋出了異常,并且析構(gòu)函數(shù)自身沒(méi)能捕獲到異常,則程序?qū)⒈唤K止。
異常對(duì)象(exception object):是一種特殊的對(duì)象,編譯器使用異常拋出表達(dá)式來(lái)對(duì)異常對(duì)象進(jìn)行拷貝初始化。因此,throw語(yǔ)句中的表達(dá)式必須擁有完全類型。而且如果該表達(dá)式是類類型的話,則相應(yīng)的類必須含有一個(gè)可訪問(wèn)的析構(gòu)函數(shù)和一個(gè)可訪問(wèn)的拷貝或移動(dòng)構(gòu)造函數(shù)。如果該表達(dá)式是數(shù)組類型或函數(shù)類型,則表達(dá)式將被轉(zhuǎn)換成與之對(duì)應(yīng)的指針類型。異常對(duì)象位于由編譯器管理的空間中,編譯器確保無(wú)論最終調(diào)用的是哪個(gè)catch子句都能訪問(wèn)該空間。當(dāng)異常處理完畢后,異常對(duì)象被銷毀。如果退出了某個(gè)塊,則同時(shí)釋放塊中局部對(duì)象使用的內(nèi)存。因此,拋出一個(gè)指向局部對(duì)象的指針幾乎肯定是一種錯(cuò)誤的行為。出于同樣的原因,從函數(shù)中返回指向局部對(duì)象的指針也是錯(cuò)誤的。當(dāng)我們拋出一條表達(dá)式時(shí),該表達(dá)式的靜態(tài)編譯時(shí)類型決定了異常對(duì)象的類型。如果一條throw表達(dá)式解引用一個(gè)基類指針,而該指針實(shí)際指向的是派生類對(duì)象,則拋出的對(duì)象將被切掉一部分,只有基類部分被拋出。
捕獲異常:catch子句(catch clause)中的異常聲明(exception declaration)看起來(lái)像是只包含一個(gè)形參的函數(shù)形參列表。像在形參列表中一樣,如果catch無(wú)須訪問(wèn)拋出的表達(dá)式的話,則我們可以忽略捕獲形參的名字。聲明的類型決定了處理代碼所能捕獲的異常類型.這個(gè)類型必須是完全類型,它可以是左值引用,但不能是右值引用。
當(dāng)進(jìn)入一個(gè)catch語(yǔ)句后,通過(guò)異常對(duì)象初始化異常聲明中的參數(shù)。和函數(shù)的參數(shù)類似,如果catch的參數(shù)類型是非引用類型,則該參數(shù)是異常對(duì)象的一個(gè)副本,在catch語(yǔ)句內(nèi)改變參數(shù)實(shí)際上改變的是局部副本而非異常對(duì)象本身;相反,如果參數(shù)是引用類型,則和其它引用參數(shù)一樣,該參數(shù)是異常對(duì)象的一個(gè)別名,此時(shí)改變參數(shù)也就是改變異常對(duì)象。
catch的參數(shù)還有一個(gè)特性也與函數(shù)的參數(shù)非常類似:如果catch的參數(shù)是基類類型,則我們可以使用其派生類類型的異常對(duì)象對(duì)其進(jìn)行初始化。此時(shí),如果catch的參數(shù)是非引用類型,則異常對(duì)象將被切掉一部分,這與將派生類對(duì)象以值傳遞的方式傳給一個(gè)普通函數(shù)差不多。另一方面,如果catch的參數(shù)是基類的引用,則該參數(shù)將以常規(guī)方式綁定到異常對(duì)象上。
異常聲明的靜態(tài)類型將決定catch語(yǔ)句所能執(zhí)行的操作。如果catch的參數(shù)是基類類型,則catch無(wú)法使用派生類特有的任何成員。
通常情況下,如果catch接受的異常與某個(gè)繼承體系有關(guān),則最好將該catch的參數(shù)定義成引用類型。
查找匹配的處理代碼:在搜索catch語(yǔ)句的過(guò)程中,我們最終找到的catch未必是異常的最佳匹配。相反,挑選出來(lái)的應(yīng)該是第一個(gè)與異常匹配的catch語(yǔ)句。因此,越是專門的catch越應(yīng)該置于整個(gè)catch列表的前端。因?yàn)閏atch語(yǔ)句是按照其出現(xiàn)的順序逐一進(jìn)行匹配的,所以當(dāng)程序使用具有繼承關(guān)系的多個(gè)異常時(shí)必須對(duì)catch語(yǔ)句的順序進(jìn)行組織和管理,使得派生類異常的處理代碼出現(xiàn)在基類異常的處理代碼之前。
與實(shí)參和形參的匹配規(guī)則相比,異常和catch異常聲明的匹配規(guī)則受到更多限制。此時(shí),絕大多數(shù)類型轉(zhuǎn)換都不被允許,除了一些極細(xì)小的差別之外,要求異常的類型和catch聲明的類型是精確匹配的:
(1)、允許在非常量向常量的類型轉(zhuǎn)換,也就是說(shuō),一條非常量對(duì)象的throw語(yǔ)句可以匹配一個(gè)接受常量引用的catch語(yǔ)句。
(2)、允許從派生類向基類的類型轉(zhuǎn)換。
(3)、數(shù)組被轉(zhuǎn)換成指向數(shù)組(元素)類型的指針,函數(shù)被轉(zhuǎn)換成指向該函數(shù)類型的指針。
除此之外,包括標(biāo)準(zhǔn)算術(shù)類型轉(zhuǎn)換和類類型轉(zhuǎn)換在內(nèi),其它所有轉(zhuǎn)換規(guī)則都不能在匹配catch的過(guò)程中使用。
如果在多個(gè)catch語(yǔ)句的類型之間存在著繼承關(guān)系,則我們應(yīng)該把繼承鏈最低端的類(most derived type)放在前面,而將繼承鏈最頂端的類(least derived type)放在后面。
重新拋出:有時(shí),一個(gè)單獨(dú)的catch語(yǔ)句不能完整地處理某個(gè)異常。在執(zhí)行了某些校正操作之后,當(dāng)前的catch可能會(huì)決定由調(diào)用鏈更上一層的函數(shù)接著處理異常。一條catch語(yǔ)句通過(guò)重新拋出(rethrowing)的操作將異常傳遞給另外一個(gè)catch語(yǔ)句。這里的重新拋出仍然是一條throw語(yǔ)句,只不過(guò)不包含任何表達(dá)式:throw;
空的throw語(yǔ)句只能出現(xiàn)在catch語(yǔ)句或catch語(yǔ)句直接或間接調(diào)用的函數(shù)之內(nèi)。如果在處理代碼之外的區(qū)域遇到了空throw語(yǔ)句,編譯器將調(diào)用terminate。
一個(gè)重新拋出語(yǔ)句并不指定新的表達(dá)式,而是將當(dāng)前的異常對(duì)象沿著調(diào)用鏈向上傳遞。
很多時(shí)候,catch語(yǔ)句會(huì)改變其參數(shù)的內(nèi)容。如果在改變了參數(shù)的內(nèi)容后catch語(yǔ)句重新拋出異常,則只有當(dāng)catch異常聲明是引用類型時(shí)我們對(duì)參數(shù)所做的改變才會(huì)被保留并繼續(xù)傳播。
捕獲所有異常的處理代碼:為了一次性捕獲所有異常,我們使用省略號(hào)作為異常聲明,這樣的處理代碼稱為捕獲所有異常(catch-all)的處理代碼,形如catch(…)。一條捕獲所有異常的語(yǔ)句可以與任意類型的異常匹配。
catch(…)通常與重新拋出語(yǔ)句一起使用,其中catch執(zhí)行當(dāng)前局部能完成的工作,隨后重新拋出異常。
catch(…)既能單獨(dú)出現(xiàn),也能與其它幾個(gè)catch語(yǔ)句一起出現(xiàn)。
如果catch(…)與其它幾個(gè)catch語(yǔ)句一起出現(xiàn),則catch(…)必須在最后的位置。出現(xiàn)在捕獲所有異常語(yǔ)句后面的catch語(yǔ)句將永遠(yuǎn)不會(huì)被匹配。
函數(shù)try語(yǔ)句塊與構(gòu)造函數(shù):通常情況下,程序執(zhí)行的任何時(shí)刻都可能發(fā)生異常,特別是異常可能發(fā)生在處理構(gòu)造函數(shù)初始值的過(guò)程中。構(gòu)造函數(shù)在進(jìn)入其函數(shù)體之前首先執(zhí)行初始值列表。因?yàn)樵诔跏贾盗斜頀伋霎惓r(shí)構(gòu)造函數(shù)體內(nèi)的try語(yǔ)句塊還未生效,所以構(gòu)造函數(shù)體內(nèi)的catch語(yǔ)句無(wú)法處理構(gòu)造函數(shù)初始值列表拋出的異常。要想處理構(gòu)造函數(shù)初始值拋出的異常,我們必須將構(gòu)造函數(shù)寫出函數(shù)try語(yǔ)句塊(也稱為函數(shù)測(cè)試塊,function try block)的形式。函數(shù)try語(yǔ)句塊使得一組catch語(yǔ)句既能處理構(gòu)造函數(shù)體(或析構(gòu)函數(shù)體),也能處理構(gòu)造函數(shù)的初始化過(guò)程(或析構(gòu)函數(shù)的析構(gòu)過(guò)程)。
在初始化構(gòu)造函數(shù)的參數(shù)時(shí)也可能發(fā)生異常,這樣的異常不屬于函數(shù)try語(yǔ)句塊的一部分。函數(shù)try語(yǔ)句塊只能處理構(gòu)造函數(shù)開(kāi)始執(zhí)行后發(fā)生的異常。和其它函數(shù)調(diào)用一樣,如果在參數(shù)初始化的過(guò)程中發(fā)生了異常,則該異常屬于調(diào)用表達(dá)式的一部分,并將在調(diào)用者所在的上下文中處理。
處理構(gòu)造函數(shù)初始值異常的唯一方法是將構(gòu)造函數(shù)寫成函數(shù)try語(yǔ)句塊。
noexcept異常說(shuō)明:在C++11新標(biāo)準(zhǔn)中,我們可以通過(guò)提供noexcept說(shuō)明(noexcept specification)指定某個(gè)函數(shù)不會(huì)拋出異常。其形式是關(guān)鍵字noexcept緊跟在函數(shù)的參數(shù)列表后面,用以標(biāo)識(shí)該函數(shù)不會(huì)拋出異常。
對(duì)于一個(gè)函數(shù)來(lái)說(shuō),noexcept說(shuō)明要么出現(xiàn)在該函數(shù)的所有聲明語(yǔ)句和定義語(yǔ)句中,要么一次也不出現(xiàn)。該說(shuō)明應(yīng)該在函數(shù)的尾置返回類型之前。我們也可以在函數(shù)指針的聲明和定義中指定noexcept。在typedef或類型別名中則不能出現(xiàn)noexcept。在成員函數(shù)中,noexcept說(shuō)明符需要跟在const及引用限定符之后,而在final、override或虛函數(shù)的=0之前。
違反異常說(shuō)明:如果一個(gè)函數(shù)在說(shuō)明了noexcept的同時(shí)又含有throw語(yǔ)句或者調(diào)用了可能拋出異常的其它函數(shù),編譯器將順利編譯通過(guò),并不會(huì)因?yàn)檫@種違反異常說(shuō)明的情況而報(bào)錯(cuò)(不排除個(gè)別編譯器會(huì)對(duì)這種用法提出警告)。一旦一個(gè)noexcept函數(shù)拋出了異常,程序就會(huì)調(diào)用terminate以確保遵守不在運(yùn)行時(shí)拋出異常的承諾。noexcept可以用在兩種情況下,一是我們確認(rèn)函數(shù)不會(huì)拋出異常;二是我們根本不知道該如何處理異常。
通常情況下,編譯器不能也不必在編譯時(shí)驗(yàn)證異常說(shuō)明。
如果函數(shù)被設(shè)計(jì)為是throw()的,則意味著該函數(shù)將不會(huì)拋出異常:void f(int) throw();
異常說(shuō)明的實(shí)參:noexcept說(shuō)明符接受一個(gè)可選的實(shí)參,該實(shí)參必須能轉(zhuǎn)換為bool類型:如果實(shí)參是true,則函數(shù)不會(huì)拋出異常;如果實(shí)參是false,則函數(shù)可能拋出異常。
noexcept運(yùn)算符:noexcept說(shuō)明符的實(shí)參常常與noexcept運(yùn)算符(noexcept orerator)混合使用。noexcept運(yùn)算符是一個(gè)一元運(yùn)算符,它的返回值是一個(gè)bool類型的右值常量表達(dá)式,用于表示給定的表達(dá)式是否會(huì)拋出異常。和sizeof類似,noexcept也不會(huì)求其運(yùn)算對(duì)象的值。
noexcept有兩層含義:當(dāng)跟在函數(shù)參數(shù)列表后面時(shí)它是異常說(shuō)明符;而當(dāng)作為noexcept異常說(shuō)明的bool實(shí)參出現(xiàn)時(shí),它是一個(gè)運(yùn)算符。
異常說(shuō)明與指針、虛函數(shù)和拷貝控制:盡管noexcept說(shuō)明符不屬于函數(shù)類型的一部分,但是函數(shù)的異常說(shuō)明仍然會(huì)影響函數(shù)的使用。函數(shù)指針及該指針?biāo)傅暮瘮?shù)必須具有一致的異常說(shuō)明。也就是說(shuō),如果我們?yōu)槟硞€(gè)指針做了不拋出異常的說(shuō)明,則該指針將只能指向不拋出異常的函數(shù)。相反,如果我們顯示或隱式地說(shuō)明了指針可能拋出異常,則該指針可以指向任何函數(shù),即使是承諾了不拋出異常的函數(shù)也可以。
如果一個(gè)虛函數(shù)承諾了它不會(huì)拋出異常,則后續(xù)派生出來(lái)的虛函數(shù)也必須做出同樣的承諾;與之相反,如果基類的虛函數(shù)允許拋出異常,則派生類的對(duì)應(yīng)函數(shù)既可以允許拋出異常,也可以不允許拋出異常。
當(dāng)編譯器合成拷貝控制成員時(shí),同時(shí)也生成一個(gè)異常說(shuō)明。如果對(duì)所有成員和基類的所有操作都承諾了不會(huì)拋出異常,則合成的成員是noexcept的。如果合成成員調(diào)用的任意一個(gè)函數(shù)可能拋出異常,則合成的成員是noexcept(false)。而且,如果我們定義了一個(gè)析構(gòu)函數(shù)但是沒(méi)有為它提供異常說(shuō)明,則編譯器將合成一個(gè)。合成的異常說(shuō)明將與假設(shè)由編譯器為類合成析構(gòu)函數(shù)時(shí)所得的異常說(shuō)明一致。
異常類層次:標(biāo)準(zhǔn)庫(kù)異常類構(gòu)成了下圖所示的繼承體系:
類型exception僅僅定義了拷貝構(gòu)造函數(shù)、拷貝賦值運(yùn)算符、一個(gè)虛析構(gòu)函數(shù)和一個(gè)名為what的虛成員。其中what函數(shù)返回一個(gè)const char*,該指針指向一個(gè)以null結(jié)尾的字符數(shù)組,并且確保不會(huì)拋出任何異常。
類exception、bad_cast和bad_alloc定義了默認(rèn)構(gòu)造函數(shù)。類runtime_error和logic_error沒(méi)有默認(rèn)構(gòu)造函數(shù),但是有一個(gè)可以接受C風(fēng)格字符串或者標(biāo)準(zhǔn)庫(kù)string類型實(shí)參的構(gòu)造函數(shù),這些實(shí)參負(fù)責(zé)提供關(guān)于錯(cuò)誤的更多信息。在這些類中,what負(fù)責(zé)返回用于初始化異常對(duì)象的信息。因?yàn)閣hat是虛函數(shù),所以當(dāng)我們捕獲基類的引用時(shí),對(duì)what函數(shù)的調(diào)用將執(zhí)行與異常對(duì)象動(dòng)態(tài)類型對(duì)應(yīng)的版本。
實(shí)際的應(yīng)用程序通常會(huì)自定義exception(或者exception的標(biāo)準(zhǔn)庫(kù)派生類)的派生類以擴(kuò)展其繼承體系。這些面向應(yīng)用的異常類表示了與應(yīng)用相關(guān)的異常條件。和其它繼承體系一樣,異常類也可以看作按照層次關(guān)系組織的。層次越低,表示的異常情況就越特殊。例如,在異常類繼承體系中位于最頂層的通常是exception,exception表示的含義是某處出錯(cuò)了,至于錯(cuò)誤的細(xì)節(jié)則未作描述。
繼承體系的第二層將exception劃分為兩個(gè)大的類別:運(yùn)行時(shí)錯(cuò)誤和邏輯錯(cuò)誤。運(yùn)行時(shí)錯(cuò)誤表示的是只有在程序運(yùn)行時(shí)才能檢測(cè)到的錯(cuò)誤;而邏輯錯(cuò)誤一般指的是我們可以在程序代碼中發(fā)現(xiàn)的錯(cuò)誤。
下面是從其他文章中copy的std::exception測(cè)試代碼,詳細(xì)內(nèi)容介紹可以參考對(duì)應(yīng)的reference:
#include "exception.hpp"
#include <exception>
#include <typeinfo>
#include <iostream>
#include <utility>
#include <cstring>// Fix: error C3646: 'noexcept' : unknown override specifier
#ifndef _MSC_VER
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endifnamespace exception_ {
/
// reference: http://www.cplusplus.com/reference/exception/exception/
class Polymorphic { virtual void member(){}
};int test_exception_1()
{try {Polymorphic * pb = 0;typeid(*pb); // throws a bad_typeid exception} catch (std::exception& e) { // std::std::bad_typeidstd::cerr << "exception caught: " << e.what() << '\n'; // exception caught: Attempted a typeid of Null pointer!}return 0;
}struct ooops : std::exception {const char* what() const NOEXCEPT /*noexcept*/ override{ return "Ooops!\n"; }
};int test_exception_2()
{ooops e;std::exception* p = &e;try {throw e; // throwing copy-constructs: ooops(e)} catch (std::exception& ex) {std::cout << ex.what(); // 0oops!}try {throw *p; // throwing copy-constructs: std::exception(*p)} catch (std::exception& ex) {std::cout << ex.what(); // Unknown exception}return 0;
}// text_exception uses a dynamically-allocated internal c-string for what():
class text_exception : public std::exception {
public:text_exception(const char* text) {text_ = new char[std::strlen(text) + 1];std::strcpy(text_, text);}text_exception(const text_exception& e) {text_ = new char[std::strlen(e.text_) + 1];std::strcpy(text_, e.text_);}~text_exception() NOEXCEPT /*throw()*/ {delete[] text_;}const char* what() const NOEXCEPT /*noexcept*/ override { return text_; }private:char* text_;
};int test_exception_3()
{try {throw text_exception("custom text\n");} catch (std::exception& ex) {std::cout << ex.what(); // custom text}return 0;
}} // namespace exception_
GitHub: https://github.com/fengbingchun/Messy_Test
?
總結(jié)
以上是生活随笔為你收集整理的C++/C++11中std::exception的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 逻辑回归(Logistic Regres
- 下一篇: C++/C++11中std::runti