catch站点的异常常见用法是什么?

时间:2013-08-13 04:48:58

标签: c++ exception c++11 exception-handling try-catch

我对异常处理的理解非常有限。虽然我发现很容易抛出异常(或者我可以使用expected<T>将其打包以供以后使用),但我对如何处理异常几乎一无所知。

目前我的知识仅限于

  • 清理自己的资源并重新抛出要在适当位置处理的异常。 e.g。

    ptr p = alloc.allocate(n);
    try
    {
       uninitialized_copy(first,last,p);//atomic granularity, all or none
    }
    catch(...)
    {
        alloc.deallocate(p,n);
        throw;
    }
    

但我想,这可以用RAII模式等效转换为

alloc_guard<ptr> p{alloc.allocate(n)};
uninitialized_copy(first,last,p.get());
p.commit();
  • 在顶层捕捉异常,撰写&amp;打印一条好消息然后退出.g。

    int main(int argc,char** argv)
    {
       try
       {
           app_t the_app(argc,argv);
           the_app.run();
       }
       catch(std::runtime_error& e)
       {
          //instead of what, I can also compose the mesage here based on locale.
          std::cout<<e.what()<<std::endl;
       }
    }
    

所以,我所做的只是在顶级函数中,例如main捕获异常并打印相应的消息并关闭。

在使用各种外部库作为实现的后端实现具有一组良好API的库时,我意识到第三方库异常是我的API规范的一部分,因为它们跨越我的库边界并落在用户代码中! / p>

因此,我的库API泄露了我用于用户代码的外部库(并且每个都有自己的异常层次结构)的所有异常。

这导致了我的问题,当我发现任何异常时可以做些什么?

更具体地说,

  • 我可以将捕获的异常从外部库转换为我自己的异常并以通用方式抛出(比如第三方库异常层次结构和我的异常API之间的映射是以mpl::map提供的吗?)
  • 我可以做一些比打印消息/调用堆栈更有用的东西,比如使用不同的输入参数恢复抛出站点的功能(比如当我得到file_not_founddisk_error时,使用不同的文件运行该函数)?
  • 任何其他有价值的模式?

由于

3 个答案:

答案 0 :(得分:4)

除了nogard所说的我想添加以下内容:

  1. 例外应该用于特殊事物。不应该发生的事情。
  2. 如果遇到异常,请至少在某处记录。这可能有助于您找到错误。
  3. 尝试在捕获异常时解决错误。
  4. 如果无法做到这一点,请尝试保持应用程序可以继续的一致状态。
  5. 如果无法做到这一点 - 请考虑优雅地终止。
  6. 通知用户发生了一些与众不同的事情。
  7. 最终建议 - 保持错误处理的一致性。这包括将第三方库中的异常转换为异常层次结构。


    评论答案:

    2)例外应包含有关出错的信息。这可能只是类型或一些其他信息。通过在客户处记录这些信息,您可以获得有关客户告诉您的实际问题的更多信息。也许他误用了你的应用程序,发现了另一个用例,或者你只是有一个bug(例如一个未初始化的变量)。但是,通过这些额外的信息,您可以找到出错的位置以及出现问题的一些信息。这有助于您推断出错误的来源,从而找到错误。

    3)这实际上取决于正在发生的错误。例如。您尝试访问不存在的配置文件 - &gt;您使用默认值创建一个新的。客户端尝试打开数据库以进行写访问,但它是写保护的。您拒绝打开,返回到有效状态并告诉客户端数据库是写保护的。你内存不足而无法继续?记录下来(请注意 - 您没有备用内存,因此您的日志记录应该已经为此用例预先保留了一些内存)并正常关闭应用程序。也许,如果可能的话,通知客户。

    关于来自其他库的代码:除了它可能返回并捕获它们的异常之外,没有其他方法可以检查对另一个库的每个函数调用。一旦被捕获,您可以将该异常中的信息传输到您的一个中并抛出该异常(或以其他方式解决)

答案 1 :(得分:2)

这是一个非常大的主题。

  1. 我怀疑您可以轻松地将第三方异常转换为您自己的异常,而且我个人认为没有必要实现此行为。由于第三方库是您的实现的一部分,而未暴露给公共API,为什么要公开它的所有异常(甚至通过一些映射)?如果有一天你坚持使用另一个实现相同内容的第三方库 - 你想重新设计整个异常层次结构吗?我想不是。您的图书馆的API不能是脆弱的,所以我建议不要将外部例外映射到您自己的例外。

  2. 您可以按照以下方式将第三方例外映射到您的层次结构:

    • 根本不要包装任何东西。我的意思是你不必抛出任何东西只是因为第三个库这样做。您可以捕获该异常并处理它,或返回错误代码,或者适当地更改状态。还有许多其他的可能性,而不是总是重新抛出。

    • 您不必为所有第三方例外进行一对一翻译。如果您在内部使用库AAA,那么您可以使用单个AAAException来表示来自该库的许多异常。

  3. 有价值的知道:总是通过const引用捕获异常:

    catch (const exception & ex)

  4. 这个主题非常大,我希望我的回答有助于理解它。

    评论的答案:

    1. 如果我没有将第三方例外映射到我自己的API(不一定是一对一),它们会泄漏到客户端代码 - 不,他们没有,那就是整点!你必须在你的库中捕获它们,然后决定如何处理catched异常:抛出你自己的异常,返回错误代码,通知客户端监听器,记录错误等......

      try {
          3rdpatry.call();
      } catch (const 3rdpartyException & ex) {
          // throw YourException(ex.what());
          // listener.notify(some_error)
          // return some_code
      }
      
    2. 通过const引用捕获并不是为了防止切片。这里有很好的discussion解释了这一点。

答案 2 :(得分:2)

对于较大的应用程序,顶级捕获通常是不够的。当你可以对它做些什么时,你需要捕获异常,但主要只有几种方法可以处理异常:

  1. 如果可以的话,恢复。 - (例如:检查更新 - &gt;网络连接无法打开例外 - &gt;忽略并且现在不下载更新。)
  2. 告诉用户选择如何恢复。 (例如:保存文件 - &gt;文件无法创建例外 - &gt;告诉用户选择不同的文件名或取消)
  3. 记录并退出。这是顶级的全能方案。