2010-09-10 57 views
4

因此,我嘗試基於Java Exception類爲C++編寫一個簡單的基本Exception類。 我確定已經有很好的圖書館了,但是我正在做這個練習,而不是生產代碼,而且我很好奇並且總是期待學習。 Java的例外所做的一件事,我也想實現,就是'原因'的概念。在Java中,有一個原因的新異常的樣子:在C++中實現異常類

Exception cause = new Exception(); 
Exception newExcept = new Exception(cause); 

然而,在C++中,傳遞一個異常作爲參數傳遞給構造函數是拷貝構造函數是如何被調用。因此,複製Exception和創建一個新的Exception與原因之間存在概念上的脫節。這顯然不是Java中的問題。

我想我只是想知道處理這個問題的最好方法是什麼。有幾個想法,我所做的是:

  • 區分具有虛擬變量
  • 只需創建新的異常,並呼籲setCause()方法
  • 像拷貝構造函數的東西是Exception(Exception &)與事業構造是Exception(Exception *)

感謝

回答

5

異常 - 當分配到堆棧上時(我強烈推薦這一點) - 在catch子句之後被釋放。所以你需要在新創建的異常中創建一個「內部」異常的副本。如果你捕捉到異常的基類,除非你給你的異常一個克隆方法,否則它將失去它的正確類型。

#include <string> 
#include <exception> 

class MyBaseException : public std::exception 
{ 
public: 
    MyBaseException(const std::string& what = std::string("MyBaseException")) 
     : m_BaseException(0), m_What(what) {} //Constructor without inner exception 

    MyBaseException(const MyBaseException& innerException, const std::string& what = std::string("MyBaseException")) 
     : m_BaseException(innerException.clone()), m_What(what) {} //Constructor with inner exception 

    template <class T> // valid for all subclasses of std::exception 
    MyBaseException(const T& innerException, const std::string& what = std::string("MyBaseException")) 
     : m_BaseException(new T(innerException)), m_What(what) {} 

    virtual ~MyBaseException() throw() 
     { if(m_BaseException) { delete m_BaseException; } } //don't forget to free the copy of the inner exception 
    const std::exception* base_exception() { return m_BaseException; } 
    virtual const char* what() const throw() 
     { return m_What.c_str(); } //add formated output for your inner exception here 
private: 
    const std::exception* m_BaseException; 
    const std::string m_What; 
    virtual const std::exception* clone() const 
     { return new MyBaseException(); } // do what ever is necesary to copy yourselve 
}; 

int main(int argc, char *argv[]) 
{ 
    try { 
     try { 
      throw std::exception(); 
     } 
     catch(const std::exception& e) { 
      throw MyBaseException(e, "bad"); 
     } 
    } 
    catch (const MyBaseException& e) { 
     throw MyBaseException(e, "even worse"); 
    } 
    //throw MyBaseException(1, "will not compile"); 
} 
+0

IIRC拋出的異常並未分配到堆棧上,而是放在一個用於存放拋出的異常的「特殊」存儲區中,因爲當異常正在傳播時,堆棧被解除。 – 2010-09-10 12:13:48

+0

@matteo大概你是對的。它們是「特殊的」,因爲在調用catch塊時已經發生堆棧展開。可能這是編譯器特定的。 – 2010-09-10 13:36:38

+0

您需要使用智能指針指向經理m_BaseException,因爲異常對象會被複制到整個地方。目前,您可能會多次刪除基本異常指針(以及遞歸鏈)。 – 2010-09-10 13:38:50

2

你可以使用工廠模式:

Exception cause = Exception.Create(); 
Exception newExcept = Exception.Create(Exception cause); 
+0

我其實也很喜歡這個解決方案。這是相當優雅和側面的,但我選擇的答案可能有點接近我所期待的。 – 2010-09-10 13:57:15

1

只是原因異常的字符串添加到當前異常:

try 
{ 
    throw std::runtime_error("Failed to work"); 
} 
catch(std::exception const& e) 
{ 
    // New exception (add origianl exception text). 
    throw std::runtime_error(std::string("We were doing poobar when: ") + e.what()); 
} 
+0

只是要挑剔,看起來不像一個正確的carch聲明,但它看起來像一個很好的catch語句。 – HandyGandy 2010-09-10 05:30:44

0

你的問題還不清楚,甚至不似乎是Java的正確,但被支承在Java成語圖書館。我猜你所描述的習慣是將原始異常作爲參數傳遞給重新拋出時創建的異常。

的解決方案是創建一個library_exception(或任何你想將它命名)

class library_exception: public std::exception 
{ 
... 
public: 
    library_exception(const std::exception &e) 
... 
} 
... 
catch(const std::exception &e) 
{ 
    ... 
    throw library_exception(e); 
} 

不同類別沒有名爲拷貝構造函數。