我一直相信,如果一个方法可以抛出异常,那么不使用有意义的try块来保护这个调用就是鲁莽的。

我刚刚发布了‘你应该总是包装调用,可以抛出try, catch块。,结果被告知这是一个“非常糟糕的建议”——我想知道为什么。


当前回答

因为下一个问题是“我已经捕获了一个异常,接下来我该做什么?”你会怎么做?如果你什么都不做——这是错误隐藏,程序可能“就是不能工作”,没有任何机会发现发生了什么。您需要了解捕获异常后要做什么,并且只有在知道的情况下才进行捕获。

其他回答

You don't need to cover every block with try-catches because a try-catch can still catch unhandled exceptions thrown in functions further down the call stack. So rather than have every function have a try-catch, you can have one at the top level logic of your application. For example, there might be a SaveDocument() top-level routine, which calls many methods which call other methods etc. These sub-methods don't need their own try-catches, because if they throw, it's still caught by SaveDocument()'s catch.

这样做很好,有三个原因:它很方便,因为只有一个地方可以报告错误:SaveDocument()捕获块。没有必要在所有子方法中重复这一点,而且这正是您想要的:在一个单一的位置为用户提供关于出错的有用诊断。

第二,每当抛出异常时,保存将被取消。对于每个尝试捕获子方法,如果抛出异常,则进入该方法的捕获块,执行离开函数,并通过SaveDocument()继续进行。如果事情已经出了问题,你可能会想就此打住。

第三,所有子方法都可以假设每次调用都成功。如果调用失败,执行将跳转到catch块,后续代码永远不会执行。这可以使您的代码更加清晰。例如,下面是错误代码:

int ret = SaveFirstSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveSecondSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveThirdSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

下面是如何编写例外情况:

// these throw if failed, caught in SaveDocument's catch
SaveFirstSection();
SaveSecondSection();
SaveThirdSection();

现在发生了什么更加清楚了。

注意,以其他方式编写异常安全代码可能更加棘手:如果抛出异常,您不希望泄漏任何内存。确保你了解RAII、STL容器、智能指针和其他在析构函数中释放资源的对象,因为对象总是在异常之前被析构。

尽管Mike Wheat的回答很好地总结了要点,但我还是觉得有必要再补充一个答案。我是这样想的。当你有方法做很多事情时,你是在增加复杂性,而不是增加它。

换句话说,封装在try catch中的方法有两种可能的结果。有非异常结果和异常结果。当你处理很多方法的时候这个指数级的爆炸超出了你的理解。

因为如果每个方法都以两种不同的方式分支,那么每次调用另一个方法时,你都是在对之前的潜在结果数进行平方。当你调用了5个方法时,你至少有256个可能的结果。与此相比,在每个方法中都不执行try/catch,您只有一条路径可以遵循。

我基本上就是这么看的。您可能会认为任何类型的分支都做同样的事情,但try/catch是一个特殊情况,因为应用程序的状态基本上是未定义的。

简而言之,try/catch使代码更难理解。

我的计算机科学教授曾经给我的建议是:“只有在使用标准方法无法处理错误时,才使用Try and Catch块。”

作为一个例子,他告诉我们,如果一个程序在一个地方遇到了一些严重的问题,而不可能做这样的事情:

int f()
{
    // Do stuff

    if (condition == false)
        return -1;
    return 0;
}

int condition = f();

if (f != 0)
{
    // handle error
}

然后你应该使用try, catch块。虽然您可以使用异常来处理这个问题,但通常不建议这样做,因为异常会消耗大量性能。

除了上面的建议,我个人使用尝试+抓+扔的方法;原因如下:

At boundary of different coder, I use try + catch + throw in the code written by myself, before the exception being thrown to the caller which is written by others, this gives me a chance to know some error condition occured in my code, and this place is much closer to the code which initially throw the exception, the closer, the easier to find the reason. At the boundary of modules, although different module may be written my same person. Learning + Debug purpose, in this case I use catch(...) in C++ and catch(Exception ex) in C#, for C++, the standard library does not throw too many exception, so this case is rare in C++. But common place in C#, C# has a huge library and an mature exception hierarchy, the C# library code throw tons of exception, in theory I(and you) should know every exceptions from the function you called, and know the reason/case why these exception being thrown, and know how to handle them(pass by or catch and handle it in-place)gracefully. Unfortunately in reality it's very hard to know everything about the potential exceptions before I write one line of code. So I catch all and let my code speak aloud by logging(in product environment)/assert dialog(in development environment) when any exception really occurs. By this way I add exception handling code progressively. I know it conflit with good advice but in reality it works for me and I don't know any better way for this problem.

正如在其他回答中所述,只有在可以对异常进行某种合理的错误处理时才应该捕获异常。

例如,在生成您的问题的问题中,提问者询问忽略从整数到字符串的lexical_cast异常是否安全。这样的阵容永远不会失败。如果它失败了,说明程序中出现了严重的错误。在这种情况下,你能做些什么来恢复呢?最好的方法可能是让程序死亡,因为它处于不可信任的状态。因此,不处理异常可能是最安全的做法。