2009-09-26 102 views
19

試想兩個相似的代碼段:拋出和拋出arg捕獲異常有什麼區別?

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw err; 
} 

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw; 
} 

有效這些是相同的,還是他們在一些微妙的方式有什麼不同?例如,第一個是否會導致拷貝構造函數被運行,而第二個可能會重用同一個對象來重新拋出呢?

回答

26

取決於你如何安排你的異常層次結構,重新拋出由throw語句命名異常變量的異常可能原來的異常對象。

一個無參數的擲表達將拋出當前異常對象保持其動態型,而用自變量的擲表達將根據靜態類型的參數throw的拋出一個新的異常。

E.g.

int main() 
{ 
    try 
    { 
     try 
     { 
      throw Derived(); 
     } 
     catch (Base& b) 
     { 
      std::cout << "Caught a reference to base\n"; 
      b.print(std::cout); 
      throw b; 
     } 
    } 
    catch (Base& b) 
    { 
     std::cout << "Caught a reference to base\n"; 
     b.print(std::cout); 
    } 

    return 0; 
} 

如上寫入時,程序將輸出:

Caught a reference to base 
Derived 
Caught a reference to base 
Base

如果throw b是具有throw替換,則外抓也將趕上原來拋出Derived異常。如果內部類通過值而不是引用捕獲異常,這仍然成立 - 儘管自然這意味着原始異常對象不能被修改,所以對b的任何更改都不會反映在由外部塊捕獲的Derived異常中。

+1

啊,我完全忘了切片!該死的,這很重要!感謝您提出這個問題。 +1(儘管我認爲當你寫下「...保留原始靜態類型...」時,你的意思是_dynamic_type。所謂_dynamic type_,畢竟如果不是_「original static type」_。) - – sbi 2009-09-26 23:58:36

+1

太棒了回答,我也完全忘記了這一點。 – GManNickG 2009-09-27 00:10:29

+0

我很高興別人遇到_slicing_問題;) – 2009-09-27 00:37:22

16

在根據C++標準15.1/6拷貝構造第二種情況下不使用:

的throw-表達與沒有操作數的重新拋出正在處理的異常。該例外情況在現有的臨時情況下重新激活;沒有新的臨時異常對象被創建。這個例外不再被認爲是被捕獲的;因此,uncaught_exception()的值將再次爲真。

在第一種情況新異常將根據15.1/3被拋出:

的throw-表達初始化臨時對象,稱爲異常對象,其中所述類型的通過去除任何確定從「T的數組」或「返回T的函數」到「指向T的指針」或「返回T的函數的指針」的類型,分別爲 和 的靜態類型的頂級cv限定符。 < ...>臨時用於初始化匹配處理程序(15.3)中指定的變量。除了void *,const void *, volatile void *或const volatile void *以外,throw-expression的類型不應爲 不完整類型,或指向不完整類型的指針或引用。除了這些限制和對15.3中提到的 類型匹配的限制之外,throw的操作數在調用 (5.2.2)或返回語句的操作數時完全作爲函數參數處理。

在兩種情況下複製構造需要在擲階段(15.1/5):

當拋出的對象是一個類對象,和用於初始化臨時副本拷貝構造是不可訪問,該程序是不合格的(即使臨時對象可能被刪除)。 同樣,如果該對象的析構函數不可訪問,則該程序是不合格的(即使臨時對象可能被刪除)。

+0

由於無論如何都需要訪問原始引發的異常,因此我認爲在這種情況下這不是問題。但你的答案仍然很好。 +1 – sbi 2009-09-26 17:18:32

+0

是的,但在第一種情況下複製c-tor將被使用兩次。 – 2009-09-26 17:20:04

+1

它說什麼時候聲明指定了一個類的類型。但在他的情況下,它指定了一個引用類型:)引用將被直接綁定並引用異常對象。因此,我們只需要在投擲時需要一個副本,而不是在這種情況下捕獲(例如,當基本副本構造函數受保護時,這會有所不同)。 – 2009-09-26 17:25:42