2017年計算機(jī)二級C++實例編程:c++異常機(jī)制

字號:


    知識點(diǎn): c++異常機(jī)制
    一、 概述
    C++自身有著非常強(qiáng)的糾錯能力,發(fā)展到如今,已經(jīng)建立了比較完善的異常處理機(jī)制。C++的異常情況無非兩種,一種是語法錯誤,即程序中出現(xiàn)了錯誤的語句,函數(shù),結(jié)構(gòu)和類,致使編譯程序無法進(jìn)行。另一種是運(yùn)行時發(fā)生的錯誤,一般與算法有關(guān)。
    關(guān)于語法錯誤,不必多說,寫代碼時心細(xì)一點(diǎn)就可以解決。C++編譯器的報錯機(jī)制可以讓我們輕松地解決這些錯誤。
    第二種是運(yùn)行時的錯誤,常見的有文件打開失敗、數(shù)組下標(biāo)溢出、系統(tǒng)內(nèi)存不足等等。而一旦出現(xiàn)這些問題,引發(fā)算法失效、程序運(yùn)行時無故停止等故障也是常有的。這就要求我們在設(shè)計軟件算法時要全面。比如針對文件打開失敗的情況,保護(hù)的方法有很多種,最簡單的就是使用“return”命令,告訴上層調(diào)用者函數(shù)執(zhí)行失敗;另外一種處理策略就是利用c++的異常機(jī)制,拋出異常。
    二、c++異常處理機(jī)制
    C++異常處理機(jī)制是一個用來有效地處理運(yùn)行錯誤的非常強(qiáng)大且靈活的工具,它提供了更多的彈性、安全性和穩(wěn)固性,克服了傳統(tǒng)方法所帶來的問題.
    異常的拋出和處理主要使用了以下三個關(guān)鍵字: try、 throw 、 catch 。
    拋出異常即檢測是否產(chǎn)生異常,在C++中,其采用throw語句來實現(xiàn),如果檢測到產(chǎn)生異常,則拋出異常。該語句的格式為:
    throw 表達(dá)式;
    如果在try語句塊的程序段中(包括在其中調(diào)用的函數(shù))發(fā)現(xiàn)了異常,且拋棄了該異常,則這個異常就可以被try語句塊后的某個catch語句所捕獲并處理,捕獲和處理的條件是被拋棄的異常的類型與catch語句的異常類型相匹配。由于C++使用數(shù)據(jù)類型來區(qū)分不同的異常,因此在判斷異常時,throw語句中的表達(dá)式的值就沒有實際意義,而表達(dá)式的類型就特別重要。
    try-catch語句形式如下 :
    try
    {
    包含可能拋出異常的語句;
    }
    catch(類型名 [形參名]) // 捕獲特定類型的異常
    {
    }
    catch(類型名 [形參名]) // 捕獲特定類型的異常
    {
    }
    catch(...) // 三個點(diǎn)則表示捕獲所有類型的異常
    {
    }
    【范例1】處理除數(shù)為0的異常。該范例將上述除數(shù)為0的異??梢杂胻ry/catch語句來捕獲異常,并使用throw語句來拋出異常,從而實現(xiàn)異常處理,實現(xiàn)代碼如代碼清單1-1所示。
    // 代碼清單1-1
    1 #include //包含頭文件
    2 #include
    3 double fuc(double x, double y) //定義函數(shù)
    4 {
    5 if(y==0)
    6 {
    7 throw y; //除數(shù)為0,拋出異常
    8 }
    9 return x/y; //否則返回兩個數(shù)的商
    10 }
    11 void main()
    12 {
    13 double res;
    14 try //定義異常
    15 {
    16 res=fuc(2,3);
    17 cout<<"The result of x/y is : "<
    18 res=fuc(4,0); //出現(xiàn)異常,函數(shù)內(nèi)部會拋出異常
    19 }
    20 catch(double) //捕獲并處理異常
    21 {
    22 cerr<<"error of dividing zero.\n";
    23 exit(1); //異常退出程序
    24 }
    25 }
    【范例2】自定義異常類型 (在本文開始的代碼中已經(jīng)給出示范)
    三、異常的接口聲明
    為了加強(qiáng)程序的可讀性,使函數(shù)的用戶能夠方便地知道所使用的函數(shù)會拋出哪些異常,可以在函數(shù)的聲明中列出這個函數(shù)可能拋出的所有異常類型,例如:
    void fun() throw( A,B,C,D);這表明函數(shù)fun()可能并且只可能拋出類型(A,B,C,D)及其子類型的異常。
    如果在函數(shù)的聲明中沒有包括異常的接口聲明,則此函數(shù)可以拋出任何類型的異常,例如:void fun();
    一個不會拋出任何類型異常的函數(shù)可以進(jìn)行如下形式的聲明:
    void fun() thow();
    四、異常處理中需要注意的問題
    1. 如果拋出的異常一直沒有函數(shù)捕獲(catch),則會一直上傳到c++運(yùn)行系統(tǒng)那里,導(dǎo)致整個程序的終止
    2. 一般在異常拋出后資源可以正常被釋放,但注意如果在類的構(gòu)造函數(shù)中拋出異常,系統(tǒng)是不會調(diào)用它的析構(gòu)函數(shù)的,處理方法是:如果在構(gòu)造函數(shù)中要拋出異常,則在拋出前要記得刪除申請的資源。
    3. 異常處理僅僅通過類型而不是通過值來匹配的,所以catch塊的參數(shù)可以沒有參數(shù)名稱,只需要參數(shù)類型。
    4. 函數(shù)原型中的異常說明要與實現(xiàn)中的異常說明一致,否則容易引起異常沖突。
    5. 應(yīng)該在throw語句后寫上異常對象時,throw先通過Copy構(gòu)造函數(shù)構(gòu)造一個新對象,再把該新對象傳遞給 catch.
    那么當(dāng)異常拋出后新對象如何釋放?
    異常處理機(jī)制保證:異常拋出的新對象并非創(chuàng)建在函數(shù)棧上,而是創(chuàng)建在專用的異常棧上,因此它才可以跨接多個函數(shù)而傳遞到上層,否則在棧清空的過程中就會被銷毀。所有從try到throw語句之間構(gòu)造起來的對象的析構(gòu)函數(shù)將被自動調(diào)用。但如果一直上溯到main函數(shù)后還沒有找到匹配的catch塊,那么系統(tǒng)調(diào)用terminate()終止整個程序,這種情況下不能保證所有局部對象會被正確地銷毀。
    6. catch塊的參數(shù)推薦采用地址傳遞而不是值傳遞,不僅可以提高效率,還可以利用對象的多態(tài)性。另外,派生類的異常撲獲要放到父類異常撲獲的前面,否則,派生類的異常無法被撲獲。
    7. 編寫異常說明時,要確保派生類成員函數(shù)的異常說明和基類成員函數(shù)的異常說明一致,即派生類改寫的虛函數(shù)的異常說明至少要和對應(yīng)的基類虛函數(shù)的異常說明相同,甚至更加嚴(yán)格,更特殊。