我是否仍然可以在catch块中调用的函数中重新抛出异常?

时间:2015-10-05 09:56:44

标签: c++ try-catch rethrow

我在遗留代码库中有以下结构:

try{
...
}
catch(Type1&){
...
}
catch(Type2&){
...
}
...

通过复制粘贴开发,相同的catch块随处可见。 现在,我会编写一个这样的函数:

void catchErrors(){
  try{
    throw;
  }
  catch(Type1&){
    ...
  }
  ...
}

并将其放入代码中:

try{
  ...
}
catch(...){
  catchErrors();
}

这是一个有效的重构器,导致相同的功能吗?
(你对重构有什么更好的建议吗?)

2 个答案:

答案 0 :(得分:5)

是的,这是有效的。

  

[C++14: 15.1/8]:没有操作数的 throw-expression 重新抛出当前处理的异常(15.3)。使用现有异常对象重新激活该异常;没有创建新的异常对象。唯一的例外是否定的   更长时间被认为被捕;因此,std::uncaught_exception()的值将再次成立。

     

[例如:由于异常而必须执行但仍无法完全处理异常的代码可以这样编写:

try {
  // ...
} catch (...) { // catch all exceptions
  // respond (partially) to exception
  throw; // pass the exception to some
  // other handler
}
     

-end example]

     

[C++14: 15.1/9]:如果当前没有处理异常,则执行不带操作数的throw-expression调用std::terminate()(15.5.1)。

虽然 throw-expression 已被移入其自己的函数中,但在执行期间仍然处理异常,因此它仍然有效:

#include <iostream>

void bar()
{
    try {
        throw;
    }
    catch (int x) {
        std::cerr << "Damn " << x << "!\n";
    }
}

void foo()
{
    try {
        throw 42;
    }
    catch (...) {
        bar();
    }
}

int main()
{
    foo();
}

// Output: Damn 42!

live demo

答案 1 :(得分:3)

是的,您的重构是有效的。实际上,它是一种相当古老的技术,专门用于将异常处理程序集移动到另一个函数中,并允许它们重用。

请注意,如果在异常处理块之外调用CatchErrors(),则调用std::terminate()将调用throw;。如果没有处理异常,则需要main()语句。

只是不要太依赖这项技术。最好通过一些异常安全保证来设计大多数函数(即,如果抛出异常并且它们不调用它们它们将不会出现故障)。这样可以更有可能集中处理异常处理(例如在gcc -o prog prog.c -lm -fopenmp中),而不是拥有许多必须分别处理异常的不同功能。这样做可以减少重用异常处理代码的需要,因为大多数异常只能在一个地方处理。