2009-10-22 63 views
17

我一直在嘗試爲正在處理的C++庫製作一些自定義異常類。這些自定義異常會捕獲調試所需的額外信息,例如文件,行號等,如果由於某種原因而在測試異常時沒有找到正確的位置。然而,大多數人似乎建議繼承STL中的std :: exception類,我同意這一點,但我想知道使用多繼承來繼承每個derived std::exception classes(例如std :: runtime_error )和一個自定義異常類,如下面的代碼所示?C++中的自定義異常

另一件事,如何才能在異常類中複製構造函數和賦值運算符?他們應該被禁用?

class Exception{ 
    public: 
     explicit Exception(const char *origin, const char *file, 
          const int line, const char *reason="", 
          const int errno=0) throw(); 

     virtual ~Exception() throw(); 

     virtual const char* PrintException(void) throw(); 

     virtual int GetErrno(void); 

    protected: 
     std::string m_origin; 
     std::string m_file; 
     int m_line; 
     std::string m_reason; 
     int m_errno; 
}; 

class RuntimeError: public virtual std::runtime_error, public Exception{ 
    public: 
       explicit RuntimeError(const char *origin, const char *file, 
            const int line, const char *reason="", 
            const int errno=0) throw(); 
     virtual ~RuntimeError() throw(); 
}; 
+0

你會從中得到什麼?爲什麼不使用單繼承?爲什麼你需要自定義* Exception和RuntimeError異常? – jalf 2009-10-22 09:40:40

+0

我想使用我自定義的Exception類中的功能,並通過繼承各種派生的std :: exception類來維護STL異常層次結構。通過這種方式搭配: 嘗試throw RunTimeException(...); (std :: runtime_error&e){ ... } catch(std :: exception&e){...} 也會捕獲我的自定義類。 雖然這可能是浪費時間。我也可以只用 嘗試throw RunTimeException(...); } catch(std :: exception&e){...} 但它可能會使識別異常更困難。自定義異常類時,我不知道典型的做法是什麼? – Tom 2009-10-22 10:16:05

+0

我會說只是從std :: runtime_error(或std :: exception)繼承。如果你想要一個特定的治療,你可以有多個捕獲。 – 2009-10-22 18:36:39

回答

11

你應該嘗試boost::exception

加速異常的目的是爲了緩解 異常類 層次的設計和幫忙寫 異常處理和錯誤報告 代碼。

它支持任意 數據傳輸到catch網站,這是 否則棘手由於沒有擲 要求(15.5.1)異常 類型。當異常對象 向上傳播調用堆棧時,可以將數據添加到任何異常對象,直接在 的throw-expression(15.1)或 以後的時間。

他們已經傳遞到 後扔將數據添加到異常 物體的能力是非常重要的,因爲經常的來處理 異常所需要的一些信息 是在檢測到故障的情況下 不可用。

加速異常也支持異常 對象 N2179式的複製,實現非侵入 並通過 的boost :: throw_exception功能自動。

+0

我從來沒有使用過提升。我會研究它。感謝這篇文章。 – Tom 2009-10-22 09:57:39

+1

非常棒的圖書館。現在我希望我以前讀過它! – 2009-10-22 19:01:28

17

我在想,也許這將是更好地使用多重繼承,從每一個派生的std ::的異常類繼承

請注意,這是一個問題,因爲這樣的事實:標準庫中的例外從彼此非虛擬地派生。如果您引入了多重繼承,您會得到沒有虛擬繼承的可怕鑽石異常層次結構,並且由於派生異常類攜帶兩個子對象std::exception,因此std::exception爲「模糊基類」,因此將無法通過std::exception&捕獲派生異常。

具體的例子:

class my_exception : virtual public std::exception { 
    // ... 
}; 

class my_runtime_error : virtual public my_exception 
         , virtual public std::runtime_error { 
    // ... 
}; 

現在my_runtime_errorstd::exception導出(間接地)的兩倍,通過my_exception一旦通過std::run_time_error和一次。因爲前者不從std::exception幾乎獲得,這

try { 
    throw my_runtime_error(/*...*/); 
} catch(const std::exception& x) { 
    // ... 
} 

將無法​​正常工作。

編輯:

我想我見過的Stroustrup的書之一,涉及MI的異常類層次結構的第一個例子,所以我的結論是,在一般情況下,這是一個好主意。標準庫的例外不會從對方中導出,我認爲是失敗。

當我最後設計了一個異常層次結構時,我非常廣泛地使用了MI,但並沒有從std lib的異常類中派生出來。在這個層次結構中,有您定義的抽象異常類,以便您的用戶可以捕獲它們,以及相應的實現類,這些實例類是從這些抽象類和實現基類派生的,您將實際拋出這些類。爲了使這更容易,我定義了一些模板,會做所有的辛勤工作:

// something.h 
class some_class { 
private: 
    DEFINE_TAG(my_error1); // these basically define empty structs that are needed to 
    DEFINE_TAG(my_error2); // distinguish otherwise identical instances of the exception 
    DEFINE_TAG(my_error3); // templates from each other (see below) 
public: 
    typedef exc_interface<my_error1> exc_my_error1; 
    typedef exc_interface<my_error2> exc_my_error2; 
    typedef exc_interface<my_error3,my_error2> // derives from the latter 
            exc_my_error3; 

    some_class(int i); 
    // ... 
}; 

//something.cpp 
namespace { 
    typedef exc_impl<exc_my_error1> exc_impl_my_error1; 
    typedef exc_impl<exc_my_error2> exc_impl_my_error2; 
    typedef exc_impl<exc_my_error3> exc_impl_my_error3; 
    typedef exc_impl<exc_my_error1,exc_my_error2> // implements both 
            exc_impl_my_error12; 
} 
some_class::some_class(int i) 
{ 
    if(i < 0) 
    throw exc_impl_my_error3(EXC_INFO // passes '__FILE__', '__LINE__' etc. 
          , /* ... */ // more info on error 
          ); 
} 

回首現在這個樣子,我想我可以作出exc_impl類模板從std::exception獲得(或任何其他類std lib異常層次結構,作爲可選的模板參數傳遞),因爲它永遠不會從任何其他exc_impl實例派生。但當時這不是必需的,所以它從來沒有發生過。

+0

+1:很有說服力 – fmuecke 2009-10-22 09:09:10

+0

感謝帖子sbi。使用多重繼承來製作異常類是一個好習慣嗎?或者更好的只是從std :: exception類繼承? – Tom 2009-10-22 10:02:57

+0

@Tom:我加入了我的想法作爲編輯。 – sbi 2009-10-22 11:03:03