2010-04-26 173 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”块。