【问题标题】:Is the lifetime of an exception affected by nested handlers?异常的生命周期是否受嵌套处理程序的影响?
【发布时间】:2011-12-21 19:17:06
【问题描述】:

考虑以下代码sn-p:

struct ExceptionBase : virtual std::exception{};
struct SomeSpecificError : virtual ExceptionBase{};
struct SomeOtherError : virtual ExceptionBase{};

void MightThrow();
void HandleException();
void ReportError();

int main()
{
  try
  {
    MightThrow();
  }
  catch( ... )
  {
    HandleException();
  }
}

void MightThrow()
{
  throw SomeSpecificError();
}

void HandleException()
{
  try
  {
    throw;
  }
  catch( ExceptionBase const & )
  {
    // common error processing
  }

  try
  {
    throw;
  }
  catch( SomeSpecificError const & )
  {
    // specific error processing
  }
  catch( SomeOtherError const & )
  {
    // other error processing
  }

  ReportError();
}

void ReportError()
{
}

标准中的第 15.1.4 节告诉我们:

被抛出异常的临时副本的内存是 以未指定的方式分配,除非在 3.7.3.1 中注明。这 只要有一个正在执行的处理程序,临时就会持续存在 那个例外。特别是,如果处理程序通过执行 扔;语句,将控制权传递给另一个处理程序 例外,所以暂时保留。 当最后一个处理程序被 通过 throw 以外的任何方式为异常退出执行;这 临时对象被销毁,实现可能会释放 临时对象的内存;任何此类解除分配都在 一种未指定的方式。破坏发生后立即 在异常声明中声明的对象的销毁 处理程序。

我将main 中的处理程序视为“最后一个处理程序”是否正确?因此,HandleException 中允许任意数量的重新抛出和捕获,而不会导致当前异常对象的破坏?

【问题讨论】:

  • ¤ main 中的处理程序是最后一个,是的。是的,您可以根据需要多次重新抛出和重新捕获异常。但是,这不是一个好主意。相反,让您的处理程序执行 (1) 纯异常转换,或 (2) 纯日志记录和终止。该处理程序代码自然不具备处理故障的知识。欢呼吧,
  • @Alf 谢谢。似乎我在发布一个小代码示例时省略了一些重要的细节。实际上,ExceptionBase 源自 boost::exception(从中提取了许多常见的上下文数据)。此外,main 实际上是任意数量的 COM 方法之一,因此在返回适当的 HRESULT 代码之前会记录错误(包括常见的和特定于错误的上下文数据)。但不管怎样,你已经回答了我的问题。

标签: c++ exception exception-handling


【解决方案1】:

我将 main 中的处理程序视为“最后一个处理程序”是否正确?

是的,你是。

因此在 HandleException 中允许任意数量的重新抛出和捕获,而不会导致当前异常对象的破坏?

是的。最终未处理的异常对象将被编译器生成的代码销毁。不会造成内存泄漏。

重投HandleException()不好。反而 1. 将捕获写入请求特定处理的任何异常类型; 2. 您可以使用dynamic_cast 对您的异常处理进行分组。捕获基本异常类型并尝试将其向下转换为它的任何派生异常类。但是dynamic_cast 不是好习惯。所以最好使用第一种解决方案。

最好用以下方式重写你的代码:

struct ExceptionBase : virtual std::exception{};
struct SomeSpecificError : virtual ExceptionBase{};
struct SomeOtherError : virtual ExceptionBase{};

void MightThrow();
void HandleExceptionBase();

int main()
{
  try
  {
    MightThrow();
  }
  catch (SomeOtherError &error) {
    // first common code
    HandleExceptionBase();
    // react on this exception correctly
    // specific code
  }
  catch (SomeSpecificError &error) {
    // first common code
    HandleExceptionBase();
    // react on this exception correctly
    // specific code
  }
  catch (ExceptionBase &error) {
    HandleExceptionBase();
    // finally catch anything derived from base class
    // react on this exception correctly
  }
  catch(...) {
    // react on any other exception except 3 listed above
  }
}

void MightThrow()
{
  throw SomeSpecificError();
}

void HandleExceptionBase() {
  // base exception handler
}

【讨论】:

  • 感谢您的回复。但不幸的是,它没有回答我提出的问题。随时编辑您的答案,我很乐意支持它。另请注意,您的代码的行为与原始代码的行为不同,因为如果 SomeOtherErrorSomeSpecificError 被捕获,则不会执行 ExceptionBase 的处理程序。
【解决方案2】:

感谢迄今为止发布的 cmets 和答案。我还没有完全看到我在寻找什么,所以我将在我的后续问题中添加来自 an answer provided by aschepler 的一些信息。

15.3p7:当 catch 子句的形式参数(如果有)的初始化完成时,处理程序被认为是活动的。 ... 一个处理程序 当 catch 子句退出或当 std::unexpected() 因抛出而被输入后退出。

15.3p8:最近激活的处理程序仍处于活动状态的异常称为当前处理的异常。

我认为标准的语言在这里很清楚,main 实际上是 last 处理程序。因此异常的生命周期不受嵌套处理程序的影响。

【讨论】:

    猜你喜欢
    • 2022-01-11
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多