在C ++中,为什么要重新抛出异常?为什么不让当前的catch块处理异常。出于什么原因,您会将异常重新抛出到另一个try / catch块?
答案 0 :(得分:3)
当一个函数无法满足它的契约时它会引发一个异常(它会向调用者承诺它会做什么)。当一个函数调用另一个抛出异常的函数时,有四种主要方法可以响应:
抓住异常并处理。只有在函数能够满足其合同的情况下才能执行此操作,尽管抛出了异常。如果它捕获异常但未能满足其合同,则会隐藏调用代码中的问题。
允许异常传播。如果此函数无法处理异常(即函数因抛出异常而无法满足其约定),并且异常将相应的信息公开给调用代码,则应该这样做。
抓住异常,进行一些清理和/或添加额外信息,然后重新抛出。如果此函数无法处理异常,则应该执行此操作,但在传播之前需要进行一些清理。它还可以提供额外的信息来帮助处理/调试异常(我经常认为程序员是最后一个异常处理程序)。
抓住异常并抛出异常(可能包装原件)。如果此函数无法处理异常,则应该这样做,但是另一个异常更好地表达了调用代码的问题。
答案 1 :(得分:1)
为什么不让当前的catch块处理异常。出于什么原因,您会将异常重新抛出到另一个try / catch块?
异常背后的想法是你将它们扔到错误站点并在堆栈中处理它们,在那里你有足够的信息来处理错误。
相反,有些情况下您必须在出现错误时执行某些操作,但仍然不知道如何处理错误(当您重新抛出时就是这种情况)。
示例:
void connect_and_notify(int connection_data)
{
try
{
create_network_connection(connection_data); // defined somewhere else
notify("connection open"); // same (notify event listeners)
}
catch(const std::runtime_error&)
{
notify("connection failed");
throw;
}
}
客户代码:
void terminal_app_controller()
{
try
{
connect_and_notify(1);
}
catch(const std::runtime_error& err)
{
std::cerr << "Connection failed;\n";
exit(1); // this is usually bad bad code but whatever
}
}
void ongoing_server_controller()
{
bool connected = false;
int connection = 1;
while(!connected)
{
try
{
connect_and_notify(1);
connected = true;
}
catch(const std::runtime_error&)
{
connection++;
}
}
}
在两种使用方案中,错误的处理方式不同(connect_and_notify
无法知道,但是,在失败的连接上,它必须通知侦听器。)
每个函数都有不同的策略来处理异常,这意味着不同的catch块。
答案 2 :(得分:0)
我非常不喜欢
之类的东西catch (std::exception&) {
... // do some cleanup
throw;
}
RAII是解决该问题的正确方法。甚至:
catch (std::exception&) {
... // do some logging here
throw;
}
可以使用RAII处理,但不太直观。
但是 - 我有重新抛出的地方是第3部分(或供应商提供的)代码抛出“泛型”异常状态的任何情况。例如,在将远程信息处理消息记录到数据库时,我知道我经常会收到同一消息的重复副本。每条消息都有一个唯一的ID - 因此我的数据库中的主键违规是一个“无辜的”错误,应该被忽略。
不幸的是,我们使用的DB框架没有为PK违规抛出特定的异常 - 所以我们需要捕获泛型dbexception并检查它的原因代码是什么来决定做什么。因此:
catch (db::exception& e) {
if (e.reason != db::exception::reason::pk_violation)
throw;
}
此外,piwi提到了内部状态。一个例子是
for (;;) {
try {
...
}
catch (some_exception& e) {
if (retry_count > 3)
throw;
}
}
请记住:如果要重新抛出,请始终通过引用捕获以避免切片异常对象。 (无论如何,你通常应该通过ref捕获它,但在重新抛出时更为重要)