【问题标题】:Throwing a new exception while throwing an old exception在抛出旧异常的同时抛出新异常
【发布时间】:2010-04-26 20:48:29
【问题描述】:

如果由于异常导致堆栈展开期间析构函数在 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.

如果在 Java 中由于相应的 try 块中的异常而进入 finally 块,并且该 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"
    }
}

我想到了这个问题:一种编程语言能否处理同时抛出的多个异常?那会有用吗?你有没有错过这个能力?有没有一种语言已经支持这个?有这种方法的经验吗?

有什么想法吗?

【问题讨论】:

  • 你刚刚让我的大脑抛出异常
  • 有趣的问题。我假设“处理异常”是指“由于异常而展开堆栈”,而不是“从 catch 块执行代码”。后者我称之为“处理异常”,但由于已找到处理程序,您可以从那里抛出异常(至少在 C++ 中)。
  • @Nick 你说得对,我编辑了标题。如果您知道更好的,请随时再次更改它;-)
  • finally 块总是在 try 块退出时执行。无论是否抛出异常,finally 块都会执行。第一个异常没有被默默吞下,它被捕获到了 catch 块中。
  • @Lucass 肯定被吞了。我刚刚为您添加了一个 Java 示例 ;-)

标签: java c++ exception-handling


【解决方案1】:

从流量控制的角度考虑。例外基本上只是花哨的setjmp/longjmpsetcc/callcc。异常对象用于选择要跳转到的特定位置,例如地址。异常处理程序只是在当前异常longjmping 上递归,直到它被处理为止。

一次处理两个异常只是将它们捆绑在一起,这样结果就会产生连贯的流控制。我可以想到两种选择:

  • 将它们组合成一个不可捕获的异常。这相当于展开整个堆栈并忽略所有处理程序。这会产生异常级联导致完全随机行为的风险。
  • 不知何故构造了他们的笛卡尔积。是的,没错。

C++ 方法很好地满足了可预测性的利益。

【讨论】:

    【解决方案2】:

    您可以链接异常。 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){
        }
    }
    

    编辑:

    你也可以有多个捕获。 我不认为多个异常是一个好主意,因为异常已经是您需要从中恢复的东西。我能想到的有多个异常的唯一原因是,如果您将其用作逻辑的一部分(例如多次返回),这将偏离异常概念的最初目的。 另外,你怎么能同时产生两个异常呢?

    【讨论】:

    • 我相信 C# 也允许链接。我想知道 c++0x 是否允许链接?
    • 这在所有 C++ 中都是允许的。 OP 所述的 C++ 问题无关。
    【解决方案3】:

    一种编程语言可以处理多个异常吗?当然,我不明白为什么不这样做。这会有用吗?不,我会说不会。错误处理和恢复是非常困难的——我看不出在问题中添加组合爆炸会有什么帮助。

    【讨论】:

      【解决方案4】:

      是的,一种语言可以支持一次抛出多个异常;但是,这也意味着程序员也需要同时处理多个异常,因此肯定需要权衡。我听说过有这种情况的语言,尽管我无法从头顶想出清单;我相信 LINQ 或 PLINQ 可能是其中一种语言,但我不太记得了。无论如何,可以通过不同的方式抛出多个异常......一种方式是使用异常链,或者通过强制一个异常成为另一个异常的“原因”或“previouslyProgatingException”,或者将所有异常装瓶成单个异常,表示已引发多个异常的事实。我想一种语言也可以引入一个 catch 子句,让您一次指定多个异常类型,尽管这将是一个糟糕的设计选择,恕我直言,因为处理程序的数量足够大,这将导致catch 子句只是为了处理每一个可能的组合。

      【讨论】:

        【解决方案5】:

        C++ std::exception_ptr 允许您存储异常。因此,应该可以将异常嵌入到其他异常中,并给您一种印象,即您拥有抛出异常的堆栈。如果您想知道实际异常的根本原因,这可能很有用。

        【讨论】:

        • 这适用于在 catch 块内抛出的异常。在展开期间从析构函数抛出的异常是另一回事。
        • 我知道。但题与此无关,只是对最后一道题的介绍。
        【解决方案6】:

        多个并行抛出的异常可能有用的一种情况是使用 JUnit 进行单元测试:

        • 如果测试失败,则会引发异常(由被测代码或断言产生)。
        • 每个@After 方法在测试后被调用,无论测试失败还是成功。
        • 如果 After 方法失败,则会引发另一个异常。
        • 只有 After 方法中抛出的异常会显示在我的 IDE (Eclipse) 中以获取测试结果。

        我知道 JUnit 会通知它的测试侦听器这两个异常,在 Eclipse 中调试测试时,我可以看到 JUnit 视图中出现第一个异常,不久之后被第二个异常替换。

        这个问题应该可以通过让 Eclipse 记住给定测试的所有通知来解决,而不仅仅是最后一个。拥有“并行异常”,即来自finally 的异常不会吞噬来自try 的异常,也可以解决这个问题。

        【讨论】:

          【解决方案7】:

          如果你仔细想想,你所描述的情况从概念上讲,Exception("First")Exception("second") 的根本原因。对用户来说最有用的可能是获取堆栈转储,以该顺序显示链......

          【讨论】:

            【解决方案8】:

            在托管平台中,我可以想到让处置器将异常“提升”到更强大但对应用程序而言并非完全致命的异常的情况。例如,“命令”对象的处理程序可能会尝试展开其关联连接的状态以取消任何部分执行的命令。如果可行,底层代码可能会尝试对连接执行其他操作。如果尝试的“取消”不起作用,则异常可能应该传播到连接被破坏的级别。在这种情况下,包含“内部异常”的异常可能很有用,尽管我知道实现这一点的唯一方法是在 catch 块而不是“finally”块中尝试展开。

            【讨论】:

              猜你喜欢
              • 2013-05-24
              • 2012-01-24
              • 2011-05-30
              • 1970-01-01
              • 2013-07-24
              • 1970-01-01
              • 2011-02-25
              相关资源
              最近更新 更多