【问题标题】:How to avoid writing repeated code in catch blocks?如何避免在 catch 块中编写重复代码?
【发布时间】:2012-05-02 16:01:10
【问题描述】:

我在桌面应用项目中使用 QT 4.8 (C++),编写异常处理如下:

void callerMethod()
{
  try
  {
   method1();
  }
  catch(Exception1& e)
  {
    // display critcal error message
    // abort application
  }
  catch(std::Exception& e)
  {
   // print exception error message
  }
  catch(...)
  {
   // print unknown exception message
  } 
}

void method1()
{
  try
  {
   // some initializations
   // some operations (here exceptions can occur)
   // clean-up code (for successful operation i.e no exception occurred)
  }
  catch(Exception1& e)
  {
   // clean-up code
   throw e;
  }
  catch(Exception2& e)
  {
   // clean-up code
   throw e;
  }
  catch(Exception3& e)
  {
   // clean-up code
   throw e;
  }
  catch(...)
  {
   // clean-up code
   throw;
  }
}

所以我的问题是我需要在每个 catch 块中编写清理代码吗? 有什么办法可以避免重复编写代码

注意:: [ In method1() ] 我想重新抛出发生的异常 给我的来电者。所以我无法在单个捕获块中捕获它们, 因为那样类型信息将会丢失。

【问题讨论】:

  • 尝试通过使用智能指针、容器类等来减少清理代码的数量。理想情况下根本不应该有清理代码。
  • 看来你想做的只是try { /* may throw */ } catch(specific_exception const& e) { /* terminate */ }。如果您不关心异常类型 Exception1Exception2 等等,那么不要捕获它们。
  • 此外,即使您确实通过引用捕获,您应该使用throw; 而不是throw e; 重新抛出以防止切片。

标签: c++ optimization exception-handling try-catch throw


【解决方案1】:

您应该尽可能低地抛出异常,并在调用链中尽可能高地捕获它们。这会自动减少代码重复,并集中错误处理。你在一个地方扔/接,这似乎有点……强迫。

我经常做这种事情(尤其是程序结束异常:

int main()
try
{
    function_calls_that_may_throw();
    // ...
}
catch(my_exception& e)
{
    e.do_exception_stuff();
}
catch(std::exception& e)
{
    std::cout << e.what();
}
catch(...)
{
    std::cout << "Something bad happened.\n";
}

这仅适用于抛出您不打算更好地处理的异常或重试失败的操作等。

这种方法的优点是所有/大多数错误处理代码都位于程序的顶层,调用链中的所有函数都不必担心这些东西,它们所做的只是当他们愿意时抛出异常。

【讨论】:

    【解决方案2】:

    方法1可以通过两个概念大大简化:

    1. RAII。将任何清理代码放入析构函数中,清理代码将被集中。
    2. 使用不合格的throw,你就不需要知道抛出的异常类型。

    所以,method1() 应该是这样的:

    void method1()
    {
         // some initializations of RAII objects
         // some operations (here exceptions can occur)
    }
    

    如果您从std::exception 派生Exception1,则可以删除callerMethod 中的第一个catch 子句,因为what() 方法是虚拟的。

    【讨论】:

    • 但我想在 callerMethod() 中单独处理 Exception1。如果发生 Exception1,我想中止应用程序。
    • @AnwarShaikh:你仍然可以这样做。 RAII 只解决您的清理问题。
    • 好的。知道了!!单个 catch(std::exception& e) 并使用 what() 我想我可以获得异常类型。而且我不知道 RAII,它似乎可以解决我的清理问题。我会对此进行调查。 RAII 的任何好的资源链接都会有所帮助。谢谢你.. Thiton 和 Nawaz。
    • @AnwarShaikh:已链接到适当的教程。
    • @AnwarShaikh:您可以阅读 RAII 成语here,它已经简短地解释了它。并且this paper 有很长的解释。
    【解决方案3】:

    如果您的所有清理代码完全相同,您可以在 catch (...) 块中执行所有操作:

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

    如果您的代码略有不同,您可以随时调用清理函数:

    try {
       // code
    } catch (exc1 ex) {
       cleanup(args);
       // exc1 specific
       throw;
    } catch (exc2 ex) {
       cleanup(args);
       // exc2 specific
       throw;
    } catch (...) {
       cleanup(args);
       throw;
    }
    

    【讨论】:

    • 如果我编写单个 catch(..) 引发异常,那么在调用者方法中我将如何知道发生了哪个异常。因为我想处理导致应用程序中止的 Exception1。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 2019-02-26
    • 1970-01-01
    • 2014-10-05
    • 2011-08-29
    相关资源
    最近更新 更多