2010-04-26 172 views
5

如果一個析構函數在由異常引起的堆棧展開期間拋出C++,程序將終止。 (這就是爲什麼析構函數不應該用C扔++。)例:拋出一個新的異常拋出一箇舊的異常

struct Foo 
{ 
    ~Foo() 
    { 
     throw 2; // whoops, already throwing 1 at this point, let's terminate! 
    } 
}; 

int main() 
{ 
    Foo foo; 
    throw 1; 
} 

terminate called after throwing an instance of 'int' 

This application has requested the Runtime to terminate it in an unusual way. 
Please contact the application's support team for more information. 

如果finally塊被輸入在Java中,因爲在相應try塊的異常,並且finally塊拋出了第二個例外,第一個例外是默默吞下。例如:

public static void foo() throws Exception 
{ 
    try 
    { 
     throw new Exception("first"); 
    } 
    finally 
    { 
     throw new Exception("second"); 
    } 
} 

public static void main(String[] args) 
{ 
    try 
    { 
     foo(); 
    } 
    catch (Exception e) 
    { 
     System.out.println(e.getMessage()); // prints "second" 
    } 
} 

這個問題在我腦海中浮現:編程語言是否可以處理同時拋出多個異常?這會有用嗎?你有沒有錯過這個能力?有沒有一種語言已經支持這個?有沒有這種方法的經驗?

有什麼想法?

+7

你剛剛讓我的大腦拋出異常 – 2010-04-26 20:51:01

+1

有趣的問題。我通過「處理異常」來假設你明確地意思是「由於異常導致堆棧展開」,而不是「從catch塊執行代碼」。後者我會稱之爲「處理異常」,但是由於處理程序已經定位,所以可以從那裏拋出一個異常(至少在C++中)。 – 2010-04-26 20:57:22

+0

@尼克你是對的,我編輯了標題。如果你知道更好的一個,隨時可以再次更改;-) – fredoverflow 2010-04-26 20:59:19

回答

5

從流量控制角度考慮。無論如何,例外從根本上來說僅僅是花式的setjmp/longjmpsetcc/callcc。異常對象用於選擇要跳轉到的特定位置,如地址。異常處理程序簡單地在當前異常處遞歸,longjmp ing直到處理完。

一次處理兩個異常只是簡單地將它們捆綁在一起,以便結果產生一致的流量控制。我可以考慮兩種方案:

  • 將它們組合成不可捕捉的異常。這相當於展開整個堆棧並忽略所有處理程序。這會造成異常級聯的風險,導致完全隨機的行爲。
  • 以某種方式構建它們的笛卡爾積。是的,沒錯。

C++方法充分體現了可預測性的重要性。

0

是的,語言一次可以支持拋出多個異常;然而,這也意味着程序員也需要同時處理多個異常,所以這肯定是一種折衷。我聽說有這樣的語言,雖然我無法提出我頭頂的名單;我相信LINQ或PLINQ可能是這些語言之一,但我不太記得。無論如何,有多種不同的方式可以拋出多個異常......一種方法是使用異常鏈,或者強制一個異常成爲另一個異常的「原因」或「priorProgatingException」,或者將所有異常成單個異常,代表已拋出多個異常的事實。我想一種語言也可以引入一個catch子句,它允許你一次指定多個異常類型,儘管這會是一個糟糕的設計選擇,恕我直言,因爲處理程序的數量足夠大,並且這會導致捕捉子句只是爲了處理每一個可能的組合。

2

編程語言是否可以處理多個異常?當然,我不明白爲什麼不。這會有用嗎?不,我會說它不會。錯誤處理和恢復非常困難 - 我不明白如何在組合問題中添加組合爆炸會有所幫助。

3

您可以鏈接異常。 http://java.sun.com/docs/books/tutorial/essential/exceptions/chained.html

try { 

} catch (IOException e) { 
    throw new SampleException("Other IOException", e); 
} 

你也可以在你的finnally裏面嘗試一下。

try{ 
}catch(Exception e){ 
}finally{ 
    try{ 
     throw new SampleException("foo"); 
    }catch(Exception e){ 
    } 
} 

編輯:

你也可以有多個漁獲。 我不認爲多重例外是一個好主意,因爲例外已經是您需要恢復的東西。如果你將它用作邏輯的一部分(比如多個返回值),那麼我可以想到的唯一原因就是可以有多個異常,這種偏離異常思想的最初目的。 此外,你怎麼能同時產生兩個例外?

+0

我相信C#也允許鏈接。我不知道是否C++ 0x將允許鏈接? – 2010-04-26 21:13:44

+0

這在所有C++中都是允許的。 OP所陳述的C++問題是無關緊要的。 – Potatoswatter 2010-04-26 21:23:29

0

C++ std :: exception_ptr允許你存儲異常。所以應該可以在其他異常中嵌入異常,並給您一個印象,即您擁有拋出異常的堆棧。如果您想知道實際異常的根本原因,這可能很有用。

+0

這適用於在「catch」塊內引發的異常。展開期間從析構函數拋出的異常是不同的野獸。 – Potatoswatter 2010-04-26 21:14:14

+0

我知道。但問題與此無關,只是對最終問題的介紹。 – 2010-04-27 06:20:42

0

一種情形下多個並聯拋出的異常可能是有用的,是用JUnit單元測試:

  • 如果測試失敗,則拋出異常(無論被測或斷言通過代碼生產)。
  • 每個@After方法在測試之後被調用,無論測試失敗還是成功。
  • 如果After方法失敗,則會引發另一個異常。
  • 只有After方法中拋出的異常顯示在我的IDE(Eclipse)中才能顯示測試結果。

我知道JUnit的通知大約兩個異常的測試監聽器,並在Eclipse中調試測試時,我可以看到第一個例外出現在JUnit視圖,只能由第二異常後不久被替換。

這個問題應該可以通過讓Eclipse記住給定測試的所有通知來解決,而不僅僅是最後一個。在「並行例外」中,finally的例外不會吞下try中的例外,也可以解決此問題。

0

如果你仔細想一想,你所描述的情況有Exception("First")是概念上的Exception("second")的根本原因。對用戶來說最有用的東西可能是按照堆棧轉儲的順序顯示一個鏈......

0

在託管平臺中,我可以想到可能有助於讓處理程序「升級」例外情況會更強,但對於應用程序來說並非完全致命。例如,「命令」對象的disposer可能試圖展開其關聯連接的狀態以取消任何部分執行的命令。如果可行,底層代碼可能會試圖通過連接來做其他事情。如果試圖「取消」不起作用,那麼異常應該傳播到連接將被銷燬的級別。在這種情況下,對於包含「內部異常」的異常可能是有用的,儘管我知道實現這一目標的唯一方法是嘗試在catch塊中展開,而不是「finally」塊。