C++異常處理

異常的拋出

在C++中分飞,通過throw一個表達式來引發(fā)異常,被拋出的表達式的類型以及當前的調(diào)用鏈共同決定了哪段處理代碼(handler)被用來處理異常。被選中的處理代碼是在調(diào)用鏈中與拋出對象類型匹配的最近的處理代碼。

執(zhí)行throw時钉稍,throw后面的語句不會再執(zhí)行,轉(zhuǎn)而去執(zhí)行與之匹配的catch塊棺耍,該catch塊可能是同一個函數(shù)贡未,也可能位于調(diào)用了發(fā)生異常的函數(shù)的另一個函數(shù)中。

C++沒有finally蒙袍,只有Windows下的__finally俊卤。

棧展開

當throw時,首先會檢查是否位于try塊中害幅,是的話檢查與try關(guān)聯(lián)的catch塊消恍,如果找到了匹配的catch塊,就使用該catch處理異常以现,找不到則檢查外層try塊是否有匹配的catch塊狠怨,還是找不到則檢查調(diào)用當前函數(shù)的外層函數(shù)是否有匹配的catch塊佩抹,如果一直找不到,程序?qū)⑼顺鋈《@個過程被稱為棧展開。

在棧展開過程中无宿,位于調(diào)用鏈上的語句塊有可能提前退出茵汰,編譯器將負責(zé)確保在這些語句塊中創(chuàng)建的局部對象能正確釋放。

由于在棧展開過程中出于釋放局部對象可能調(diào)用析構(gòu)函數(shù)的考慮孽鸡,析構(gòu)函數(shù)不應(yīng)該拋出不能被它自身處理的異常蹂午,換句話說,如果析構(gòu)函數(shù)需要執(zhí)行某個可能拋出異常的操作彬碱,則該操作應(yīng)該被放置在try塊中自己處理掉豆胸。標準庫類型一般都能確保它們的析構(gòu)函數(shù)不會引發(fā)異常。

棧展開退出某個塊或者函數(shù)時巷疼,會同時釋放局部對象晚胡,因此throw不能拋出一個指向局部對象的指針。

執(zhí)行完catch塊后嚼沿,會找到與try塊關(guān)聯(lián)的最后一個catch塊之后的點繼續(xù)執(zhí)行估盘。

未處理的異常

如果找不到處理異常的代碼,程序會調(diào)用terminate骡尽,terminate會調(diào)用abort退出程序遣妥,你可以通過set_terminate執(zhí)行其它操作而不是調(diào)用abort。

void terminateHandler() {
    std::cout << "terminateHandler was called by terminate." << std::endl;
    system("pause");
    exit(-1);
}

int main()
{
    set_terminate(terminateHandler);
    throw std::exception();
    system("pause");
    return 0;
}

捕獲異常

catch(params list)子句用來捕獲異常攀细,如果catch無需訪問異常對象箫踩,則我們可以忽略形參名。聲明的類型決定了處理代碼所能捕獲的異常類型(使用catch(...)能捕獲所有異常)谭贪,這個類型必須是完全類型(不能只聲明境钟,不定義),可以是左值引用故河,但不能是右值引用吱韭。如果參數(shù)是引用類型,則該參數(shù)是異常對象的一個別名鱼的,反之該參數(shù)是異常對象的一個副本理盆。為避免不必要的異常對象復(fù)制和對象切片, catch 子句的最佳實踐是按引用捕捉凑阶。

異常類型也能是基本類型猿规,例如int,char*

int errorFunc()
{
    try{
        throw "error";
    }
    catch (const char* errorStr){
        std::cout << errorStr << std::endl;
    }
    return 0;
}

int main()
{
    errorFunc();
    system("pause");
    return 0;
}

由于只有第一個與異常匹配的catch塊會被執(zhí)行宙橱,所以越是專門的catch塊越應(yīng)該置于整個catch列表的前端姨俩,派生類異常的處理代碼應(yīng)該出現(xiàn)基類異常處理代碼之前蘸拔。

有時一個單獨的catch塊不能完整地處理異常,這個時候可以讓調(diào)用鏈上更上一層的函數(shù)接著處理異常环葵,方法是在catch塊中執(zhí)行throw;(不包含任何表達式)调窍。如果在throw前修改了參數(shù),則只有當參數(shù)是引用類型時张遭,我們對參數(shù)所做的改變才會被保留并繼續(xù)傳播邓萨。

處理構(gòu)造函數(shù)初始值列表拋出的異常

由于初始值列表拋出異常時,函數(shù)體內(nèi)的try語句塊還未生效菊卷,所以我們必須把構(gòu)造函數(shù)寫成函數(shù)try語句塊缔恳,代碼如下:

class Person
{
public:
    int height_;
    Person() try :height_(10)
    {
        //...
    }
    catch (...) {

    }
};

noexcept說明符

在C++11中,我們可以通過noexcept說明某個函數(shù)不會拋出異常洁闰,形式是關(guān)鍵字noexcept緊跟在函數(shù)的參數(shù)列表后面歉甚。

void func1() noexcept;//不會拋出異常
void func2()//可能拋出異常

noexcept說明符要么出現(xiàn)在函數(shù)的所有聲明和定義語句中,要么一次也不出現(xiàn)扑眉。我們也可以在函數(shù)指針的聲明和定義中指定noexcept纸泄。在typedef或類型別名中不能出現(xiàn)noexcept。在成員函數(shù)中腰素,noexcept說明符需要跟在const及引用限定符之后刃滓,而在final,override或虛函數(shù)的=0之前耸弄。

如果一個函數(shù)拋出了異常咧虎,但是又有noexcept說明符,編譯器并不會報錯计呈,只是警告而已砰诵。一旦一個noexcept函數(shù)拋出了異常,程序就會調(diào)用terminate以確保遵守不在運行時拋出異常的承諾捌显。因此noexcept用于兩種情況下茁彭,一種是我們確認函數(shù)不會拋出異常,二是我們根本不知道如何處理該異常扶歪。

noexcept說明符接受一個可選的實參理肺,該實參必須能轉(zhuǎn)換為bool類型,如果實參為true善镰,則不會拋出異常妹萨。

void func1() noexcept(true);//不會拋出異常
void func2() noexcept(false);//可能拋出異常

noexcept運算符

noexcept運算符經(jīng)常與noexcept說明符的實參混合使用,noexcept(function(...))是一個一元運算符炫欺,它的返回值是一個bool類型的右值表達式乎完,用于表達給定的表達式是否會拋出異常。

void func1() noexcept(true);//不會拋出異常
void func2() noexcept(noexcept(func1()));//不會拋出異常

noexcept與函數(shù)指針品洛,虛函數(shù)和拷貝控制

函數(shù)指針以及該指針所指的函數(shù)必須具有一致的異常說明树姨,如果某個函數(shù)指針做了不拋出異常的聲明摩桶,那么該指針只能指向不拋出異常的函數(shù),相反的如果函數(shù)指針說明了可能拋出異常帽揪,則該指針可以指向任何函數(shù)硝清。

如果一個虛函數(shù)說明它不會拋出異常,則派生類的虛函數(shù)也必須做出同樣的說明转晰,相反虛函數(shù)說明了可能拋出異常耍缴,派生類對應(yīng)的函數(shù)既可以允許拋出異常也可以不允許。

當編譯器合成拷貝控制成員時挽霉,同時也生成一個異常說明,如果對所有成員和基類都說明了不會拋出異常变汪,則合成的成員是noexcept侠坎,如果合成成員調(diào)用的任意一個函數(shù)可能拋出異常,則合成的成員是noexcept(false)裙盾。如果我們定義了一個析構(gòu)函數(shù)实胸,但是沒有為它提供異常說明,編譯器將合成一個番官。

標準庫異常類

標準庫所生成的所有異常繼承自 std::exception庐完。

捕獲系統(tǒng)異常

C++標準只要求try catch捕獲throw出來的異常,并不要求捕獲系統(tǒng)異常(如被0除徘熔,段錯誤门躯,CPU異常等)。從C++層面來說酷师,不要期望try, catch能捕獲系統(tǒng)異常讶凉。不同操作系統(tǒng)有不同的方法捕捉系統(tǒng)異常。

errorFunc()捕捉不到系統(tǒng)異常山孔,代碼如下:

int errorFunc()
{
    int p = 0;
    try{
        return *(int*)(p);
    }
    catch (...){
        std::cout << "catch error" << std::endl;
    }
}

int main()
{
    errorFunc();
    system("pause");
    return 0;
}

Windows下可以使用SEH捕捉系統(tǒng)異常懂讯,Windows下try catch也是基于SEH實現(xiàn)的。

int errorFunc()
{
    int p = 0;
    __try {
        return *(int*)(p);
    }
    __except(EXCEPTION_EXECUTE_HANDLER){
        std::cout << "catch error" << std::endl;
    }
}

int main()
{
    errorFunc();
    system("pause");
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末台颠,一起剝皮案震驚了整個濱河市褐望,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌串前,老刑警劉巖瘫里,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異荡碾,居然都是意外死亡减宣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門玩荠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漆腌,“玉大人贼邓,你說我怎么就攤上這事∶颇颍” “怎么了塑径?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長填具。 經(jīng)常有香客問我统舀,道長,這世上最難降的妖魔是什么劳景? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任誉简,我火速辦了婚禮,結(jié)果婚禮上盟广,老公的妹妹穿的比我還像新娘闷串。我一直安慰自己,他們只是感情好筋量,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布烹吵。 她就那樣靜靜地躺著,像睡著了一般桨武。 火紅的嫁衣襯著肌膚如雪肋拔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天呀酸,我揣著相機與錄音凉蜂,去河邊找鬼。 笑死性誉,一個胖子當著我的面吹牛跃惫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艾栋,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爆存,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蝗砾?” 一聲冷哼從身側(cè)響起先较,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悼粮,沒想到半個月后闲勺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡扣猫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年菜循,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片申尤。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡癌幕,死狀恐怖衙耕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情勺远,我是刑警寧澤橙喘,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站胶逢,受9級特大地震影響厅瞎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜初坠,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一和簸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碟刺,春花似錦锁保、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜜托。三九已至抄囚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間橄务,已是汗流浹背幔托。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜂挪,地道東北人重挑。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像棠涮,于是被迫代替她去往敵國和親谬哀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • |Swift|C++:-:|:-:|:-:關(guān)鍵字或類型|Error, throws, try, do - catc...
    大刀和長劍閱讀 658評論 0 1
  • 注意:本文中代碼均使用 Qt 開發(fā)編譯環(huán)境 異常處理### 1.throw表達式語法:throw 表達式 2.t...
    趙者也閱讀 1,126評論 0 0
  • 異常 程序的錯誤严肪,一種是編譯錯誤史煎,即語法錯誤 ,另一種是在運行時發(fā)生的錯誤不可預(yù)料的邏輯錯誤 可以預(yù)料的運行異常 ...
    帥碧閱讀 1,125評論 1 2
  • 今天把貓叔在讀書營終極主題營的五場分享再看一遍,因為我感覺自己有點忘了驳糯。 第一場:定位篇梭。 說實在的,當時貓叔分享的...
    草sw草閱讀 206評論 0 0
  • 最近對于負面情緒的思考 失敗和挫折往往會給我?guī)碡撁媲榫w酝枢,但是這個世界上失敗和挫折對于每一個人來說都是不可避免的恬偷。...
    tanghao_810d閱讀 124評論 0 0