【问题标题】:Exception thrown in handler functions处理函数中抛出的异常
【发布时间】:2013-07-31 12:40:53
【问题描述】:

我正在阅读应用 C++ 书中的以下文字。

可以从我们的处理函数中抛出异常吗?答案 是的,确实可以抛出错误。问题是这 异常必须在每个可能的异常规范中 遍历直到捕获到异常。如果不这样做,则 应用程序将调用 std::terminate()。对于大型系统,这 相当于为每个函数添加异常规范, 除非您完全了解应用程序的动态。它 在你的析构函数中包含所有异常也很重要; 否则,在这种情况下也会调用 std::terminate()。

在上面的文字中,我有以下问题,需要你的帮助理解。

  1. 作者所说的“异常必须在每个可能被遍历的期望规范中”是什么意思?

  2. 我的理解是析构函数我们不能使用异常。作者在你的析构函数中捕获所有异常是什么意思。

要求用简单的例子来说明

感谢您的时间和帮助。

【问题讨论】:

    标签: c++ exception-handling


    【解决方案1】:

    通常,异常规范不是一个好主意,因为当您修改规范时,它会产生很多重构和规模问题(作者所说的问题)。 java checked exceptions 就是这种情况。 C++没有检查异常,但是异常规范的问题是一样的,如果你想写一个统一的API。

    事实上 exception specifications are deprecated since C++11。 C++11 使用noexcept 说明符来确保函数不会引发任何异常。这允许某些编译器优化,当然也为函数的用户提供了保证。如果一个函数没有noexcept 说明符,它可以抛出异常,也可以不抛出。

    例外,顾名思义,例外。也就是说,使用try...catch 进行流控制是一种糟糕的编程技术。如果您的程序设计得很好,那么当抛出异常时,就意味着出现了非常非常错误的情况。即异常执行案例。通常,异常执行事件会转化为执行中止。这就是为什么函数在任何可能引发异常的地方都没有 try..catch 块的原因。

    【讨论】:

      【解决方案2】:

      当异常被抛出但未被捕获时,堆栈展开。这意味着将调用析构函数,一切都将被很好地清理。但是考虑一下:

      foo::~foo()
      {
          bar();
      }
      

      如果由于未捕获的异常而到达foo 的析构函数,而bar(); 恰好抛出,应用程序将立即终止,因为C++ 一次不能处理多个异常。

      如果我们,例如,这不会发生。吞下异常:

      foo::~foo()
      {
          try { bar(); } catch(...) { /* nom nom */ }
      }
      

      更新

      第一部分指的是这样的异常规范:

      struct foo
      {
          void bar() throw();
      }
      

      本规范的有效语法是

      throw() // does not throw any exception
      throw( list of exceptions ) // may throw one of these exceptions
      throw(...) // may throw any exception
      

      但正如 rodrigo 所提到的,这被证明是一个坏主意,因为这些规范会导致动态检查(在运行时)是否引发异常,从而显着降低性能。

      在 C++11 中,它被 noexcept 关键字替换:

      noexcept(true)
      noexcept(false)
      noexcept // identical to noexcept(true)
      

      这是您向编译器提供的保证,而不是相反。

      但现在终于要回答你的问题了。当您在某个嵌套链中指定throw 异常规范,并且在深度嵌套的函数中抛出异常时,您将不得不在此过程中更新每个函数的签名。异常规范愚蠢且不应再使用的另一个很好的理由。

      【讨论】:

      • @nijansen:感谢您的澄清。你能澄清第一个问题吗
      【解决方案3】:
      1. 异常规范被证明是一个坏主意,我认为它们在该语言的最新版本中已被弃用,所以我会忽略它。

      2. 一般来说,析构函数不能抛出任何异常。因为在抛出/捕获异常时发生的堆栈展开期间调用它们。如果堆栈展开时抛出异常,程序将中止。简单/安全的解决方案是用 try {...} catch (..} {} 块包裹析构函数体。

      【讨论】:

      • 永远不要使用或推荐任何其他人拥有不会重新抛出或中止应用程序的包罗万象的条款。
      • @VladLazarenko IMO,这与推荐无关 - 这是 OP 询问作者引用的意思,在这种情况下,“捕获所有异常”是非常明确的
      • @mel-:不,捕获所有异常意味着捕获所有异常。优秀的 C++ 开发人员永远不会使用这样的包罗万象的子句。只是为了举例,请阅读this
      • @VladLazarenko:我不建议使用包罗万象的条款,但这似乎是 OP 报价中建议的内容,这就是所要求的。
      • @rodrigo:我认为建议是明确地捕获析构函数代码中可能出现的每个异常。否则使用指定的no except 并在抛出未知内容时中止程序。但不使用包罗万象。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-24
      • 2020-02-24
      • 2013-03-23
      • 2011-12-12
      • 2022-10-14
      • 2011-10-10
      • 2011-11-23
      相关资源
      最近更新 更多