C 異常9 異常的注意事項

2022-05-23 15:12:08 字數 4347 閱讀 7544

一、先討論異常被引發後,可能導致的問題

意外異常

如果它是在帶異常規範的函式中引發的,則必須與規範列表中的某種異常匹配,否則為意外異常。在預設情況下,這將導致程式異常終止(雖然c++11摒棄了異常規範,但仍支援它,且有些現有的**使用了它)。

未捕獲異常

如果異常不是在函式中引發的,則必須捕獲它。如果沒**獲(在沒有try塊或沒有匹配的catch塊時,將出現這種情況),則異常被稱為未捕獲異常。

在預設情況下,這兩種異常將導致程式異常終止。當然可以修改程式對意外異常和未捕獲異常的反應。

未捕獲異常不會導致程式立刻異常終止。相反,程式將首先呼叫函式terminate()。在預設的情況下,terminate()呼叫abort()函式。

可以指定terminate()應呼叫的函式來修改terminate()的這種行為。

為此,可以呼叫set_terminate()函式。

set_terminate()和terminate()都是在標頭檔案exception中宣告的。

typedef void (* terminate_handler) ();

terminate_handler set_terminate(terminate_handler f) throw();  //c++98

terminate_handler set_terminate(terminate_handler f) noexcept;    //c++11

void terminate();                   //c++98

void terminate() noexcept;  //c++11

假設希望未捕獲的異常導致程式列印一條訊息,然後呼叫exit()函式,將退出狀態值設定為5。

首先,請包含標頭檔案exception。可以使用using編譯指令、適當的using宣告或std::限定符,來使其宣告可用。

#include

using namespace std;

然後,設計乙個完成上述兩種操作所需的函式,其原型如下:

void myquit()

最後,在程式的開頭,將終止操作指定為呼叫該函式

set_terminate(myquit);

現在,如果引發了乙個異常且沒有**獲,程式將呼叫terminate(),而後者將呼叫myquit()。

接下來看一下意外異常

通過給函式指定異常規範,可以讓函式的使用者知道要捕獲哪些異常。

假設函式的原型如下:

double argh() throw(out_if_bounds);

則可以這樣使用該函式:

try

catch(out_of_bounds & ex)

知道應捕獲哪些異常很有幫助,因為預設情況下,未捕獲的異常將導致程式異常終止。

原則上,異常規範應包含函式呼叫的其他函式引發的異常。例如,如果argh()呼叫了duh()函式,而後者可能引發retort物件異常,則argh()和duh()的異常規範中斗應包含retort。

除非自己編寫所有的函式,並且特別仔細。否則無法保證上述工作都已正確完成。

所以這也表明異常規範機制處理起來比較麻煩,這也是c++11將其摒棄的原因之一。

在這種情況之下,行為與未捕獲異常極其相似。如果發生意外異常,程式將呼叫unexpected()函式,

後者在預設情況下將呼叫abort()。

正如有乙個可用於修改terminate()的行為的set_terminate()函式一樣。

也有乙個可用於修改unexpected()行為的set_unexpected()函式。這些新函式也是在標頭檔案中exception中宣告的:

typedef void(* unexpected_handler) ();

unexpected_handler set_unexpected(unexpected_handler f) throw();   //c++98

unexpected_handler set_unexpected(unexpected_handler f) noexcept;    //c++11

void unexpected();                       //c++98

void unexpected() noexcepted;    //c+0x

然而,與提供給set_terminate()函式的行為相比,提供給set_unexpected()的函式的行為受到更嚴格的限制。具體地說,unexpected_handler函式可以:

1、通過呼叫terminate()(預設行為)、abort()或exit()來終止程式;

2、引發異常。

引發異常的結果取決於unexpected_handler函式所引發的異常以及引發意外異常的函式的異常規範;

1、如果新引發的異常與原來的異常規範匹配,則程式將從那裡開始進行正常處理,即尋找與新引發異常匹配的catch塊。基本上,這種方法將用預期的異常取代意外異常。

2、如果新引發的異常與原來的異常規範不匹配,且異常規範中沒有包括std::bad_exception型別,則程式將呼叫terminate()、bad_exception是從exception派生而來的,其宣告位於標頭檔案exception中。

3、如果新引發的異常與原來的異常規範不匹配,且異常規範中包括std::bad_exception型別,則不匹配的異常將被std::bad_exception異常所取代。

總之,要捕獲所有的異常(不管是預期的異常還是意外異常),則可以這樣做:

首先確保異常標頭檔案的宣告可用:

#include

using namespace std;

然後,設計乙個替代函式,將意外異常轉換為bad_exception異常,該函式的原型如下:

void myunexpected()

僅使用throw,而不指定異常將導致重新引發原來的異常。然而,如果異常規範中包含了這種型別,則該異常將被bad_exception物件所取代。

接下來在程式的開始位置,將意外異常操作指定為呼叫該函式:

set_unexpected(myunexpected);

最後,將bad_exception型別包括在異常規範中,並新增如下catch塊序列:

double argh() throw(out_of_bounds, bad_exception);

...try

catch(out_of_bounds & ex)

catch(bad_exception & ex)

二、有關異常的注意事項

從前面關於如何使用異常的討論可知,應在設計程式時就加入異常處理功能,而不是以後再新增。

這樣做有些缺點。

例如,使用異常會增加程式**,降低程式的執行速度。

異常規範不適用於模板,因為模板函式引發的異常可能隨特定的具體化而異。

異常和動態記憶體分配並非總能協同工作。

下面進一步討論動態記憶體分配和異常。

void test1(int n)

string類採用動態記憶體分配。通常,當函式結束時,將為mesg呼叫string的析構函式。

雖然throw語句過早地終止了函式。但它仍然使得析構函式被呼叫,這要歸功於棧解退。

因此在這裡,記憶體被正確地管理。

接下來看這個函式:

void test2(int n)

這裡有個問題,解退棧的時候,將刪除棧中的變數ar。但函式過早地終止意味著函式末尾的delete語句被忽略。

指標消失了,但它指向的記憶體塊未被釋放,並且不可訪問。總之,這些記憶體被洩漏了。

當然這種洩漏是可以被避免的。例如,可以再引發異常的函式中捕獲該異常,在catch塊中包含一些清理**,然後重新引發異常:

void test3(int n)

catch (exception & ex)

...delete ar;

return;

}但是這樣做,仍然會增加疏忽和產生其他錯誤的機會。另一種解決方法是使用智慧型指標模板

總之,雖然異常處理對於某些專案極為重要,但它也會增加程式設計的工作量,增大程式,降低程式的速度。

另一方面,不進行錯誤檢查的代價可能非常高。

三、異常處理

現代庫中,異常處理的複雜程度可能再創新高。

理解庫中的異常處理像學習語言本身一樣困難。

現代庫中包含的例程和模式可能像c++語法細節一樣陌生而困難。

要開發出優秀的軟體,必須花時間了解庫和類中的複雜內容,就像必須花時間學習c++本身一樣。

通過庫文件和源**了解到的異常和錯誤處理細節將使程式設計師和他的軟體受益。

事務異常注意事項

主要點 try.catch不會返回物件錯誤或者字段錯誤等型別的錯誤當 set xact abort 為 on 時,如果執行 transact sql 語句產生執行時錯誤,則整個事務將終止並回滾。當 set xact abort 為 off 時,有時只回滾產生錯誤的 transact sql 語句,而...

java異常的注意事項

我們在捕獲到異常後,在cath中列印了異常資訊,並且向上丟擲了異常,這時候異常資訊不能列印堆疊資訊,只有乙個錯誤提示,如果不呼叫initcause是無法列印出所有異常鏈的如下所示 catch exception e 以上 無法列印堆疊資訊,應該改為 catch exception e 或者在自定義異...

Java異常的注意事項

子類在覆蓋父類方法時,父類方法如果丟擲了異常 那麼子類的方法只能丟擲父類的異常或者該異常的子類 如果父類丟擲多個異常,那麼子類只能丟擲父類異常的子集 簡單來說,子類覆蓋父類的方法,只能丟擲父類的異常的子集。注意 如果父類方法沒有丟擲異常,那麼子類覆蓋時絕對不可能拋,只能try。class aexte...