2010-06-03 182 views
12

在C++中,這被認爲良好的編程習慣:C++嘗試捕捉做法

try { 
// some code 

} 
catch(someException) { 
// do something 
} 
catch (...) 
{ 

// left empty <-- Good Practice??? 
} 
+2

我從來沒有使用......除了'main'作爲最後的手段。如果我想要捕獲它們,我使用'std :: exception',至少你可以打印它並且瞭解原因 – 2010-06-03 16:41:35

+1

並且通過引用來捕獲它。 – ereOn 2011-05-20 13:38:14

回答

41

沒有!這是一個可怕的做法!

差不多隻有你應該catch (...)而不重新拋出異常將在main()捕捉任何其他未處理的異常,並在退出之前顯示或記錄錯誤。

如果你catch (...),你完全不知道拋出了什麼異常,因此你不知道是否繼續運行是安全的。

+1

因此,您將在調試程序時遇到麻煩,因爲此全部封鎖將會隱藏您的錯誤。 – mcandre 2010-06-03 14:58:59

+0

@mcandre:Visual Studio調試器提供第一次機會的異常處理,當拋出異常時該異常處理會中斷;我確信其他調試器也提供這種功能。 'main()'技巧中的'catch(...)'可以在不依賴於'std :: unexpected()'和'std :: terminate()'的情況下允許「正常」出口。 – 2010-06-03 15:03:22

+0

而我從來沒有發現調試異常很容易,因爲堆棧被你破壞的時間毀了。因此,我通常會在發生異常的地方進行斷言。 – 2010-06-03 16:05:24

7

那麼這取決於你的應用程序和你正試圖解決的問題。但總的來說,吞下未知的例外並不是一個好主意。至少,我會記錄一些東西。

編輯:諾亞·羅伯茨指出,大約只有唯一的一次,這可能是一個合理的想法是在析構函數。禁止析構函數中的異常非常重要,否則可能有多個異常處於活動狀態。這可能發生,例如,如果拋出異常,並且由於堆棧展開,調用某個析構函數。如果該析構函數拋出異常,則會有2個異常處於活動狀態。然後C++將調用std :: terminate(),默認情況下會結束您的程序。你可以爲這個條件安裝一個處理程序,但是除了記錄發生的事情之外,你可以做的事情不多。

儘管如此,即使是在析構函數中,也應該記錄catch (...)中的內容。但是,根據它是哪個析構函數,您可能沒有可用的日誌工具。但在大多數情況下,您仍然可以使用std::cerr

+0

我不認爲在析構函數中使用catch(...)是不可接受的。如果您不確切知道某個操作可能拋出的異常以及在什麼情況下,您不應該在析構函數中執行該操作。 – 2010-06-03 18:58:33

+0

你經常沒有選擇。如果你正在實現RAII,你必須撤銷你在構造函數中所做的事情。在第三方代碼中,在所有情況下拋出異常可能並不清楚。因此,通常只是「抓住(......)」來涵蓋所有情況。當然,如果可以的話,你應該記錄下來。 – 2010-06-03 20:26:22

2

這是那些「它取決於」的問題之一。如果你正在處理記錄不完善的第三方庫,它們繼續前進並定義了自己的異常,它們不會從std :: exception中繼承,你可能沒有其他選擇。然後,我只會將其留在我的代碼的調試部分。

7

不,這不是C++或任何其他語言的良好編程習慣。

沉默的失敗是不好的,遲早會咬你的。

如果你打算去catch (...)最少你應該做的就是記錄你正在做的事情。如果您不使用RAII,您可能還需要delete某些對象。

9

很好的做法就是:

try { 
// some code 

} 
catch(const someException & e) { 
// do something 
} 

因爲你知道的例外(S)你想趕上什麼。 (...)應該只在你的main()和線程的入口點(沒有異常應該離開線程),當然是catch(const std :: exception & e)。

+1

+1用於添加'const&'。 – Johnsyweb 2010-06-03 15:06:05

4

我只能想到這可能是必要的 - 雖然這仍然不意味着它是好的 - 是在一個析構函數。析構函數不能拋出,或者你會遇到比任何異常都要糟糕的問題(可能是bad_alloc)。如果沒有什麼可以做的,當某些事情拋出時,你的捕獲將是空的。你不能重新拋出,因爲你在一個析構函數中...這是你在這種情況下所能做的。

儘管如此,在那裏或某些東西的斷言將是有保證的。

當然,其他人提到過線程。我只是不做MT,所以我不知道那裏的問題。

4

飲食異常是一個壞主意。

至少,爲您的例外實施某種日誌記錄。您可能需要考慮重新拋出異常,並在錯誤消息中更好地描述問題。

2

作爲一個標準的做法,這是一個非常糟糕的主意。如果當你不知道如何處理它們時開始吞噬異常,那麼你就是一個。阻止它們在調用堆棧中更適合的地方處理,以及b。給你的程序機會繼續在一些錯誤的狀態。

但是,那就是說,作爲程序員的你已經做出了正確和合乎邏輯的判斷,認爲這是一個適當的時候吞下異常的可能性。這可能需要執行後續步驟,例如執行清理並通知用戶他們啓動的操作失敗。

底線,如果你不能確定捕捉的後果 - 不要捕捉。

0

其實它確實取決於你的整個程序打算做什麼,然而大多數情況下,吞併異常並繼續執行就像沒有任何事情發生一樣,你不需要被告知代碼實際上存在什麼問題,它在哪裏崩潰等...

BaşakBilgi

0

絕不說絕對。 我會說你應該幾乎永遠不會吞下異常。然而,這裏是我的代碼中一個地方,我都做到了沒有真正困擾我:

class _ACLASS dstream 
    : public ofstream 
{ 
//... 
    long GetSize (void); 
//... 
Protected: 
    string CheckRotation(void); 
//... 
}; 

string dstream::CheckRotation(void) 
{ 
//... 
     // rotate if they aren't the same (it's a new day) 
     // or our current file size exceeds the limit. 
     if (currDay != newDay || GetSize() > fileSizeLimit * ONE_MEGABYTE) 
     { 
//...    
      Purge(); // while all files are closed, look for purge opportunity 
      clear(); 
      open(Destination); 
//... 
} 

// Return current file size 
long dstream::GetSize (void) 
{ 
    long retval =0; 
    try { // PBI 1227 was caused by this not being trapped. 
     retval = (long)tellp(); 
    } catch (...) { 
     retval =0; 
    } // Swallow the exception if any 

    return retval; 
} 

在這裏,我只是想確定我的日誌文件已經超過1M的字節(或hwever許多M個字節允許由fileSizeLimit),如果是這樣,旋轉日誌。如果我在這裏遇到異常,我只會向調用者報告一個0字節的大小,並且調用者此時不會選擇旋轉日誌。在下一次通話中,它可能會得到一個正確的答案並通過。

我想如果tellp()每次調用都會拋出異常,我的日誌將不會旋轉,直到午夜。但我懷疑這是否會發生(我從來沒有在現場看到過)

+3

你是**而不是**只是'吞嚥'那個例外;當發生異常時,你正在做某事*將'retval'設置爲'0'。 – 2012-11-06 17:24:56