C++基礎6:異常

為什么需要異常?

  • 異常機制的處理原理
    程序會出現(xiàn)錯誤染突,尤其是不易察覺的錯誤匪傍。需要了解并解決這些錯誤。通常觉痛,程序出現(xiàn)錯誤役衡,都會強制退出,很難排除錯誤原因薪棒。

C語言如何表示錯誤

  1. 函數(shù)返回值
    • 通常手蝎,成功返回0,返回值-1榕莺。
    • 返回值為指針類型,成功返回非NULL,失敗返回值NULL棵介。
      例如:malloc()钉鸯;例外shmat()失敗返回值為MAP_INVALD(-1)
    • 其它另類的返回值
      fread()/fwrite()返回讀寫字符長度size_t,超出長度表示失敗邮辽。
  2. 全局變量errno

異常處理特點

異常提供一個錯誤專用通道唠雕。
優(yōu)點:

  1. 不干擾正常的返回值。
  2. 必須處理異常吨述。

案例

通過命令行計算兩個數(shù)字相除岩睁。

#include <iostream>
#include <sstream>
using namespace std;

int main(int argc,char* argv[]){
    istringstream iss(argv[1]); // 讀取第一個數(shù)字
    int a(0);
    iss >> a;
    iss = argv[2]; // 讀取第二個數(shù)字
    int b(0);
    iss >> b;
    cout<< a/b << endl;// 輸出數(shù)字相除
}

語法

異常分為兩個部分:拋出異常與捕獲并處理異常。

  • 拋出異常
throw 表達式;
  • 捕獲并處理異常
try {  
        // 保護代碼 包含可能拋出異常的語句揣云;  
} catch (類型名 [形參名]) {  
        // catch塊 處理異常
}  

特點

  1. 只要拋出異常捕儒,異常后的代碼不再執(zhí)行。
  2. 異常的所拋出與經(jīng)過的棧都會銷毀邓夕。

異常機制try-throw-catch的目標是問題檢測與問題解決分離

復雜一點地寫法

try {  
        // 保護代碼 包含可能拋出異常的語句刘莹;  
} catch (類型名1 [形參名]) {  
        // catch塊 處理異常
} catch (類型名2 [形參名]) {  
        // catch塊 處理異常
} catch (類型名3 [形參名]) {  
        // catch塊 處理異常
} catch(...){
        // catch塊 處理異常
}

注意

  • 異常捕獲具有類型匹配,只有相同的或者父類類型才能匹配到焚刚。
  • 如果多個catch都能接受相同異常点弯,只有最前面的一個可以接收到。
    catch(...)只能放在所有異常捕獲的最后

異常的接口聲明/異常規(guī)范

返回值類型 函數(shù)() throw(異常列表);

指定函數(shù)可以拋出何種異常矿咕,如果沒有throw(異常列表)默認可以拋出所有異常抢肛。
指定函數(shù)不拋出函數(shù),異常列表為空throw()痴腌。

那么當異常拋出后新對象如何釋放雌团?

異常處理機制保證:異常拋出的新對象并非創(chuàng)建在函數(shù)棧上燃领,而是創(chuàng)建在專用的異常棧上士聪,因此它才可以跨接多個函數(shù)而傳遞到上層,否則在棧清空的過程中就會被銷毀猛蔽。所有從trythrow語句之間構造起來的對象的析構函數(shù)將被自動調(diào)用剥悟。但如果一直上溯到main函數(shù)后還沒有找到匹配的catch塊,那么系統(tǒng)調(diào)用terminate()終止整個程序曼库,這種情況下不能保證所有局部對象會被正確地銷毀区岗。

舉例

  1. 捕獲異常
#include <iostream>
using namespace std;

void test(){
    cout << "before throw." << endl;
    throw -1;
    cout << "after throw." << endl;
}
int main(){
    try{
        test();
    }catch(int a){
        cout << "exception:" << a << endl;
    }
}
  1. 異常與局部對象析構
#include <iostream>
using namespace std;

class Test{
public:
    Test(){
        cout << "Test Init" <<endl;
    }
    ~Test(){
        cout << "Test Destroy" <<endl;
    }
};

int main(){
    try{
        Test t;
        cout << "before throw." << endl;
        throw -1;
        cout << "after throw." << endl;
    }catch(int a){
        cout << "exception:" << a << endl;
    }
}

注意事項

  1. 如果拋出的異常一直沒有函數(shù)捕獲(catch),則會一直上傳到c++運行系統(tǒng)那里毁枯,導致整個程序的終止慈缔。
  2. 一般在異常拋出后資源可以正常被釋放,但注意如果在類的構造函數(shù)中拋出異常种玛,系統(tǒng)是不會調(diào)用它的析構函數(shù)的藐鹤,處理方法是:如果在構造函數(shù)中要拋出異常瓤檐,則在拋出前要記得刪除申請的資源。
  3. 異常處理僅僅通過類型而不是通過值來(switch-case)匹配的娱节,所以catch塊的參數(shù)可以沒有參數(shù)名稱挠蛉,只需要參數(shù)類型。
  4. 函數(shù)原型中的異常說明要與實現(xiàn)中的異常說明一致肄满,否則容易引起異常沖突谴古。
  5. 應該在throw語句后寫上異常對象時,throw先通過Copy構造函數(shù)構造一個新對象稠歉,再把該新對象傳遞給 catch.
  6. catch塊的參數(shù)推薦采用地址傳遞而不是值傳遞掰担,不僅可以提高效率,還可以利用對象的多態(tài)性轧抗。另外恩敌,派生類的異常撲獲要放到父類異常撲獲的前面,否則横媚,派生類的異常無法被撲獲纠炮。
  7. 編寫異常說明時,要確保派生類成員函數(shù)的異常說明和基類成員函數(shù)的異常說明一致灯蝴,即派生類改寫的虛函數(shù)的異常說明至少要和對應的基類虛函數(shù)的異常說明相同芹啥,甚至更加嚴格,更特殊异逐。

標準異常類

exception派生

異常類 作用
logic_error 邏輯錯誤,在程序運行前可以檢測出來
runtime_error 運行時錯誤,僅在程序運行中檢測到

邏輯異常logic_error派生

異常類 作用
domain_error 違反了前置條件
invalid_argument 指出函數(shù)的一個無效參數(shù)
length_error 指出有一個超過類型size_t的最大可表現(xiàn)值長度的對象的企圖
out_of_range 參數(shù)越界
bad_cast 在運行時類型識別中有一個無效的dynamic_cast表達式
bad_typeid 報告在表達試typeid(*p)中有一個空指針p

運行時runtime_error派生

異常類 作用
range_error 違反后置條件
bad_alloc 存儲分配錯誤

嘗試捕獲邏輯異常和運行時異常

自定義異常類

  • 編碼流程
    1.繼承異常類exception
    2.實現(xiàn)接口what()
  • 代碼結構
class 異常類:public exception {    
public:    
   const char* what()const throw() {
        return 信息字符串;    
   }        
}; 

構造函數(shù)汉嗽、析構函數(shù)的異常處理

  • 構造函數(shù)可以拋出異常,此時不會調(diào)用析構函數(shù)问潭,所以如果拋出異常前猿诸,申請了資源,需要自己釋放狡忙。
  • C++標準指明析構函數(shù)不能梳虽、也不應該拋出異常。
  • C++標準規(guī)定灾茁,構造函數(shù)失敗窜觉,析構函數(shù)不會執(zhí)行。就是說在構造函數(shù)拋出異常前分配的資源將無法釋放北专。
  1. 如果析構函數(shù)拋出異常禀挫,則異常點之后的程序不會執(zhí)行,如果析構函數(shù)在異常點之后執(zhí)行了某些必要的動作比如釋放某些資源拓颓,則這些動作不會執(zhí)行语婴,會造成諸如資源泄漏的問題。
  2. 通常異常發(fā)生時,c++的機制會調(diào)用已經(jīng)構造對象的析構函數(shù)來釋放資源砰左,此時若析構函數(shù)本身也拋出異常画拾,則前一個異常尚未處理,又有新的異常菜职,會造成程序崩潰的問題青抛。

是否使用異常機制

為什么很多經(jīng)典書籍鼓勵使用異常,但是實際開發(fā)中很多C++編碼規(guī)范卻禁用異常酬核?
C++異常機制在語法上是更加優(yōu)雅的處理錯誤蜜另,但是實際上編譯出來的程序會有一些性能損失,另外錯誤地使用異常處理代碼會變得更加復雜嫡意。

編譯器選項

g++特殊編譯選項g++ -fno-exceptions

在不同的編碼規(guī)范中,對是否使用異常存在爭議蔬螟。

C語言實現(xiàn)異常機制

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市旧巾,隨后出現(xiàn)的幾起案子耸序,更是在濱河造成了極大的恐慌,老刑警劉巖鲁猩,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坎怪,死亡現(xiàn)場離奇詭異,居然都是意外死亡廓握,警方通過查閱死者的電腦和手機搅窿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隙券,“玉大人男应,你說我怎么就攤上這事∮樽校” “怎么了沐飘?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拟枚。 經(jīng)常有香客問我薪铜,道長众弓,這世上最難降的妖魔是什么恩溅? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮谓娃,結果婚禮上脚乡,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好奶稠,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布俯艰。 她就那樣靜靜地躺著,像睡著了一般锌订。 火紅的嫁衣襯著肌膚如雪竹握。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天辆飘,我揣著相機與錄音啦辐,去河邊找鬼。 笑死蜈项,一個胖子當著我的面吹牛芹关,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播紧卒,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼侥衬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跑芳?” 一聲冷哼從身側(cè)響起轴总,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎博个,沒想到半個月后肘习,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡坡倔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年漂佩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罪塔。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡投蝉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出征堪,到底是詐尸還是另有隱情瘩缆,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布佃蚜,位于F島的核電站庸娱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谐算。R本人自食惡果不足惜熟尉,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洲脂。 院中可真熱鬧斤儿,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至陕贮,卻和暖如春堕油,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肮之。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工馍迄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人局骤。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓攀圈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親峦甩。 傳聞我的和親對象是個殘疾皇子赘来,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

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

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,523評論 1 51
  • 1. 讓自己習慣C++ 條款01:視C++為一個語言聯(lián)邦 為了更好的理解C++,我們將C++分解為四個主要次語言:...
    Mr希靈閱讀 2,819評論 0 13
  • 重新系統(tǒng)學習下C++凯傲;但是還是少了好多知識點犬辰;socket;unix冰单;stl幌缝;boost等; C++ 教程 | 菜...
    kakukeme閱讀 19,943評論 0 50
  • 接著上節(jié) condition_varible 诫欠,本節(jié)主要介紹future的內(nèi)容涵卵,練習代碼地址。本文參考http:/...
    jorion閱讀 14,798評論 1 5
  • “突然好想見你呀” “我馬上到老家了” 不知今天怎么心血來潮想要見X先生荒叼,所以產(chǎn)生了上面的對話轿偎。當看到X先生說他回...
    扒小怪閱讀 535評論 0 1