是否存在使用catch all子句的情况:catch(...)是否合理?

时间:2009-12-01 09:05:16

标签: c++ exception-handling

每次我看到所有声明:

try 
{
  // some code 
}
catch (...)
{

}

始终是滥用行为。

反对使用缓存所有子句的参数是显而易见的。它将捕获任何,包括操作系统生成的异常,例如访问冲突。 由于异常处理程序无法知道它正在处理什么,因此在大多数情况下,异常将表现为模糊的日志消息或一些不连贯的消息框。

所以catch(...)似乎天生就是邪恶的。

但它仍然用C ++实现,其他语言(Java,C#)实现了类似的机制。那么有些情况下它的用法是否合理?

8 个答案:

答案 0 :(得分:6)

(1)声明捕获操作系统异常并不是真的。您对“访问冲突”一词的使用背叛了Windows背景;旧的MSVC ++版本确实如此。

(2)无论如何,catch-all行为对于具有特定目的的线程非常有用。捕获失败允许线程报告失败。没有它,程序的其他部分需要处理线程消失的可能性。它还允许您记录哪个线程失败,以及用于启动线程的参数。

答案 1 :(得分:5)

一般来说,它是合理的情况是你记录异常(或做类似的事情)或做一些清理,然后立即重新抛出

特别是在C ++中,登录catch(...)块是没有意义的,因为你没有任何方法可以获得异常,并且清理是没有意义的,因为你应该使用RAII。在析构函数中使用它似乎是唯一合法的案例。

答案 2 :(得分:4)

  

反对使用缓存所有子句的参数是显而易见的,它将捕获任何内容,包括操作系统生成的异常,例如访问冲突。由于异常处理程序无法知道它处理的内容,因此在大多数情况下,异常将表现为模糊的日志消息或一些不连贯的消息框。

如果那些相同的异常没有被抓住,你会得到......一个不连贯的消息框。

catch(...)让我至少提供我的自己的消息框(并调用自定义日志记录,保存崩溃转储,等。)。

我认为在析构函数中也有合理的catch(...)用法。析构函数不能抛出 - 好吧,我的意思是,它们可以抛出,但是如果析构函数在堆栈展开期间由于正在进行的异常而抛出程序终止,那么它们不应该允许异常逃脱。通常最好是允许第一个异常继续解除而不是终止程序。

另一种情况是在一个可以运行任意函数的工作线程中;通常,如果任务抛出异常,您不希望发生毫不客气的崩溃。工作线程中的catch(...)提供了半有序清理和关闭的机会。

答案 3 :(得分:3)

除了其他海报已经说过的内容之外,我还想提一下C ++标准中的一个不错的观点:

  

如果在a中找不到匹配的处理程序   程序,函数std :: terminate()   叫做; 是否是堆栈   在此调用之前解开   std :: terminate()是   的实施德音响奈德

(15.3 / 9)

这意味着main()和每个线程函数必须包装在一个catch-all处理程序中;否则,如果抛出未捕获的异常,甚至无法确定是否会调用自动对象的析构函数。

答案 4 :(得分:2)

catch(...)在两种情况下对我有用,这两种情况都是不合理的(我甚至不记得第二种情况)

首先是我的整体应用安全性。抛出不是从std::exception派生的异常是一种否定的,我在main()函数中只有一个:

int execute(void); // real program lies here

int main(void)
{
    try
    {
        return execute();
    }
    catch(const std::exception& e)
    {
        // or similar
        std::cerr << "Unhandled exception: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    catch(...)
    {
        std::cerr << "Unknown exception!" << std::endl;
        return EXIT_FAILURE;
    }
}

现在,它只是“只是为了以防万一”,它并非真正合理。应该没有理由进入那个捕获条款,因为这意味着有人做了一件坏事。观察陈述的真实无用; “发生了一件坏事,不知道是什么!”它只是首先崩溃的一步。

第二种用法可能是在析构函数或其他需要在异常传播之前进行手动管理的函数。这也不是一个理由,因为事情应该用RAII安全地清理自己。但是我可能因为某些原因而无法回忆起曾经使用过一次或两次,而且我再也看不出有这样的理由了。

答案 5 :(得分:2)

  1. 在代码调用的回调函数体周围需要try {...} catch(...) 不了解C ++异常(通常是C库)。

    否则,如果您使用的某个C ++库会抛出一个不派生的异常 std :: exception,它可能会导致调用代码崩溃或破坏其内部状态 相反,您应该捕获此异常并立即完成程序或 返回一些错误代码(意思是“我们注定了,我不知道为什么”,但它仍然更好 然后让C ++异常通过)。

  2. 围绕线程程序。主要是因为与1相同的原因。 而且因为否则线程失败会被忽视。

答案 6 :(得分:1)

catch(...)允许您编写代码,在该代码中,即使您没有长期完全控制代码所依赖的子模块,您也可以合法地声明您的代码不会崩溃。您的主张无异于声称除非作为滥用手段,否则不能使用此语义。也许是这样,但军事规格在这个问题上可能与你有所不同。

答案 7 :(得分:-2)

如果没有其他语言中的finally子句,

catch(...)是必要的:

try {
  ...
} catch(...) {
  cleanup...
  throw;
}

替代方案 - 使堆栈对象“拥有”所有东西 - 通常代码更多,可读性和可维护性更低。平台API通常是C,并没有方便捆绑它。

对于您无法控制的插件代码或者从稳定性角度看不信任的插件代码也很有用。它不会阻止它们崩溃,但它可能会使事情变得更加健全。

最后,有时候你真的不关心某事的结果。