2012-11-12 66 views
3

除了what()之外,我的異常類還應提供公共字段中的詳細信息嗎?除了什麼之外,C++異常還提供更多詳細信息()

例如,考慮boost::property_tree::ptree_bad_path可能會給你一個消息:

"No such node (mynode.value1)" 

訪問路徑("mynode.value1")的唯一方法是通過解析字符串。是否有反對增加額外的公共字段來攜帶這些信息的論據,即:

class ptree_bad_path : public ptree_error { 
public: 
    const std::string path; // <- additional detail by public field 
.... 

這種方法是否存在缺陷?

回答

2

從理論上講,如果您同時有兩個未處理的異常,那麼您有冒險運行程序terminate。然而,這是一個相當罕見的情況。

  • 異常的製備過程中投擲:罰款(雖然你不會得到你所期望的除外)異常的複製過程中
  • 投擲(經常省略掉,避免):崩潰
  • 崩潰
  • 除外(catch)的處理過程中投擲:開卷時灑精(畢竟,重新拋出一個不同的異常是常見的)

所以,在這裏可避免的風險是,如果你的異常的拷貝構造函數可能發生扔。通過將狀態移至異常中包含的shared_ptr來避免該問題是微不足道的。它使複製有點「特殊」(因爲它們與原始狀態分享它們的狀態),但如果它被正確記錄,它不應該引起任何悲傷。

的更大的風險是堆棧展開過程中。但只有在析構函數拋出時纔會發生。

就個人而言,我用的是例外包含:

  • 錯誤代碼(API來顯示/編碼正確,所有的錯誤信息被映射到一個代碼,它有助於中國/韓國/日本,真的)
  • 日誌信息,一些細節(翻譯其他異常,不管幫助時引起的問題,原來的錯誤的項目ID /名字!)
  • 功能/文件/線處拋出異常
  • 拋出異常的時間
  • 補充說明(附「對飛」棧展開期間)
  • 完整回溯(使用Linux的具體功能)

唯一的爭議點在這裏是在飛行位,因爲它可能會有效崩潰。另一方面,我在服務器上工作,所以崩潰很容易(並且緊急)修復,並且在過去的5年中我非常小心,不會導致崩潰(這樣;))。

注意,該方案顯然是唯一可用的,如果你使用異常稀少。如果每個任務經常拋出一打異常,則性能可能不可接受。在另一方面,各大編譯器所使用的零成本模型已經嚴厲懲罰的特殊路徑,以便...

2

您的異常類應包含處理錯誤所需的所有信息。這通常意味着標記出錯的地方以及任何必要的上下文。將路徑存儲在異常類中是一個好主意。

這種方法有什麼缺點嗎?

儘量避免讓成員自己拋出,因爲他們會調用std :: terminate。

+1

所以只是如何最好寫一個異常類的記錄,提示/鏈接,(一)存儲路徑在其中,還有(b)有一個nothrow副本?當然,它與'std :: exception'中的'what'消息是一樣的問題,但通常你不用擔心,因爲基類隱藏了它。 –

+0

@SteveJessop我不確定我是否有足夠的知識以提供最佳實踐。我所知道的一切都需要醜陋的樣板,而且很少有必要。 – Pubby

+1

@Steve:我不認爲這是在任何合理意義上的「最好的」,但你可以在運行時計算串用一個shared_ptr 甚至達到無拋出複製目標。或者類似但更高效的DIY解決方案。然後構建第一個異常對象可能會失敗,但隨後的複製不會失敗,因爲該字符串只是共享的。 –

0

您的例外可能會攜帶儘可能多的信息,因爲他們的捕手已經準備好(並願意)使用。如果您的特定應用程序可以使用這些附加信息,您可以放心編寫自己的異常類。在任何情況下,所有異常類都應繼承std::exception以確保catch子句不期望自定義異常將正常工作。

另一個問題是暴露庫上的這些類以供第三方客戶端使用。在這種情況下,您應該仔細考慮這些附加信息的好處是否超出了額外界面引入的麻煩,甚至是否可能完全不用。

編輯:作爲Pubby says,你的異常類不應該拋出,以避免不受歡迎的電話std::terminate()。一般來說,不應該拋出異常相關的代碼,這包括任何類的析構函數。

1

這只是我的看法,但如果你打算拋出一個異常,你可能想要確保它有足夠的信息讓你知道(a)是什麼導致異常,(b)異常是在哪裏拋出。

您可能(希望)不會向最終用戶顯示異常,因此記錄異常變成純粹啓用/改進可支持性的事情。所以從發展的角度來看,你基本上希望能夠儘可能地瞭解發生的事情。

當然你是對的,因爲你在這裏行走細線。你不希望在你的異常處理中有這樣複雜的代碼,它會冒着拋出它自己的異常的風險!