catch (...)
是所谓的“包罗万象”块。它将捕获任何C++ 异常。
catch(std::exception& e)
将仅捕获派生自std::exception
.
这是一个将由 catch-all 调用的异常示例,但不是第二个版本:
throw 42;
这对你来说可能看起来很奇怪,而且确实如此。要意识到的重要一点是,任何东西都可以作为 C++ 异常抛出——不仅仅是exception
s 或派生自exception
. 正如@bames53 在评论中提到的那样,没有所有异常都源自的根异常类型,就像在其他一些语言中一样。
同样重要的是要注意,一个包罗万象的块很容易被滥用。事实上,作为一般的经验法则,最好假设所有 catch-all 块都是程序缺陷。当然,编程中没有“总是”,但是当您学习使用异常时,这是一个安全的假设。
包罗万象的方块之所以是邪恶的,是因为它们通常是如何使用的。通常,一个天真的程序员会编写一个包罗万象的程序,试图捕捉任何编程错误,然后,至关重要的是,继续让程序运行,就好像什么都没发生一样。这是一场等待发生的灾难。程序状态现在是不确定的。某事,某处出了问题。您不能安全地忽略异常并继续进行,就像一切都很好。即使您的程序确实继续运行,也可能会在某处出现细微的堆损坏,从而掺杂程序的计算或其输出。当堆损坏确实发生时,作为程序员的你所希望的最好的事情就是立即崩溃。这样,您可以在损坏点获取调用堆栈和转储文件并找到并修复问题。但是当你有一个包罗万象的地方时,你就失去了发生这种腐败的所有背景。几乎不可能在代码中找到真正的缺陷。
当然,catch all 处理程序有一些有效且有价值的用途。最常见的一种是编写一个全局异常处理程序,然后重新处理throw
异常。这个全局处理程序可以启动某种故障记录,可能是通过记录错误本身,或者生成一个在失败程序之外进行记录的外部程序。通过重新抛出异常,您可以让委托人有机会处理可以处理的异常,同时允许无法处理的异常终止程序。
重新抛出异常很简单。只需throw
不带参数调用,如下所示:
catch (...)
{
// some magic
throw;
}
要记住的另一件事是,当您确实捕获异常时,通常最好捕获const
引用,而不仅仅是引用。