2009-12-06 80 views
5

我正在爲我的庫設計C++的異常層次結構。 「層次結構」是派生自std :: runtime_error的4個類。我想避免slicing problem的異常類,使複製構造函數受到保護。但顯然gcc需要在拋出它們的實例時調用拷貝構造函數,因此抱怨受保護的拷貝構造函數。 Visual C++ 8.0編譯相同的代碼很好。是否有任何可移植的方法來緩解異常類的切片問題?該標準是否說明了某個實現是否可能需要要拋出的類的拷貝構造函數?避免切片異常類型(C++)

+0

幾乎在任何地方都可以使用複製構造函數,C++標準允許使用它的用法。但是如果你希望你的代碼是可移植的,那麼異常就必須有一個可公開訪問的拷貝構造函數。 – 2009-12-06 16:57:17

+0

此外,我不知道你使用的是哪一個版本的VC++,但是你肯定需要一個拷貝構造函數 - 我剛剛測試過它。 – 2009-12-06 17:01:20

回答

6

我會避免設計一個不同於您的庫的異常層次結構。儘可能使用std::exception層次結構,總是從該層次結構中的某些內容中派生出您的異常。您可能需要特別閱讀exceptions portion of Marshall Cline's C++ FAQ - 讀取FAQ 17.6,17.9,17.1017.12

至於「強制用戶參考搭上」,我不知道這樣做的一個很好的方式。我已經在一個小時內拿出左右的打(這是週日下午)的唯一途徑是基於polymorphic throwing

class foo_exception { 
public: 
    explicit foo_exception(std::string msg_): m_msg(msg_) {} 
    virtual ~foo_exception() {} 
    virtual void raise() { throw *this; } 
    virtual std::string const& msg() const { return m_msg; } 
protected: 
    foo_exception(foo_exception const& other): m_msg(other.m_msg) {} 
private: 
    std::string m_msg; 
}; 

class bar_exception: public foo_exception { 
public: 
    explicit bar_exception(std::string msg_): 
     foo_exception(msg_), m_error_number(errno) {} 
    virtual void raise() { throw *this; } 
    int error_number() const { return m_error_number; } 
protected: 
    bar_exception(bar_exception const& other): 
     foo_exception(other), m_error_number(other.m_error_number) {} 
private: 
    int m_error_number; 
}; 

的想法是讓拷貝構造函數保護,強制用戶撥打Class(args).raise()代替的throw Class(args)。這可以讓你拋出一個多態的綁定異常,用戶只能通過引用來捕獲異常。任何企圖通過值捕獲的應該是都會有一個很好的編譯器警告。喜歡的東西:

foo.cpp:59: error: ‘bar_exception::bar_exception(const bar_exception&)’ is protected

foo.cpp:103: error: within this context

當然這一切是有代價的,因爲你可以不再使用throw明示或您將看到一個類似的編譯器警告打招呼:

foo.cpp: In function ‘void h()’:

foo.cpp:31: error: ‘foo_exception::foo_exception(const foo_exception&)’ is protected

foo.cpp:93: error: within this context

foo.cpp:31: error: ‘foo_exception::foo_exception(const foo_exception&)’ is protected

foo.cpp:93: error: within this context

總之,我會依靠編碼標準和文件說明你應該總是引用。確保您的庫捕獲通過引用處理的異常並拋出新對象(例如,throw Class(constructorArgs)throw;)。我希望其他C++程序員能夠擁有相同的知識 - 但爲了確保可以在任何文檔中添加註釋。

+0

感謝您的時間!在庫中的所有拋出都是通過一個宏來完成的,從而能夠將文件名和行號編碼爲異常,所以我可以改變宏定義來調用raise。 – shojtsy 2009-12-06 19:21:27

15

你的異常需要有一個公共的拷貝構造函數。編譯器必須能夠複製它以便異常處理工作。

你的問題的解決方案是總是通過引用而不是追趕:

try { 
    // some code... 
    throw MyException("lp0 is on fire!"); 
} catch (MyException const &ex) { 
    // handle exception 
} 

const -ness是可選的,但我一直把它放在因爲有很少需要修改的異常對象。)

+3

另外:總是拋出新創建的異常(或者至少是你知道它們是如何創建的異常)。不要通過一個取消引用的指針或引用,這可能是派生類。 – 2009-12-06 16:18:19

+0

好點。我更新了這個例子來舉一個「好」的例子。 – Thomas 2009-12-06 16:34:04

+0

我明白,例外情況應該引用。這正是我想通過隱藏複製構造函數來執行的。您的回答似乎表明圖書館作者無法強制在客戶端代碼中捕獲異常的正確方法 – shojtsy 2009-12-06 16:59:41

9

托馬斯的回答是正確的,但我也想建議你不要「設計一個異常層次結構」來浪費你的時間。設計類層次結構是一個非常糟糕的想法,尤其是當您可以簡單地從C++ Standard異常類中派生一對(並且不超過)新的異常類型時。

+2

+1。我在這裏和尼爾在一起。如果你認爲你的異常比其他異常更頻繁發生,因此需要它自己的異常類型(你可能有一個錯誤情況不是例外)。否則每個功能單元有1個異常(你如何定義功能單元是模糊的,但是比較大而不是較小)。 – 2009-12-06 17:06:18

-3

我會說不使用任何內置的C++異常代碼。如果你必須有例外,從頭開始創建你自己的。這是確保他們的行爲類似的唯一方法,更不用說以類似的方式實施了,並且直言不諱地在C++中實現異常是無能的。

0

我已經找到阻止我庫的客戶從值不正確,捕獲異常的兩個便攜式的方式從內部異常類virtual raise方法

  1. 拋出異常,並進行拷貝構造函數的保護。 (謝謝D.Shawley)
  2. 從庫中拋出派生異常併爲客戶端發佈異常基類。基類可以擁有受保護的拷貝構造函數,這隻允許捕獲它們的好方法。 (提到here爲相似的問題)

C++標準確實表明複製構造函數需要在throw點可訪問。我的配置中的Visual C++ 8.0違反了這部分標準,因爲沒有強制執行複製構造函數。在第15.1.3:

A throw-expression initializes a temporary object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from 「array of T」 or 「function returning T」 to 「pointer to T」 or 「pointer to function returning T」, respectively.

If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (12.2), then the exception in the handler can be initialized directly with the argument of the throw expression. When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated)

這個答案被張貼到OP的問題,我從問題刪除,並張貼作爲一個單獨的答案。

+0

雖然圖書館作者明智地讓圖書館用戶難以做錯事 - 但不要顛覆標準也很重要。該標準規定異常對象必須是可複製的(15.1.5),並且它明確允許按值捕獲。我會猶豫不決。 – 2014-11-27 14:39:07