异常的生命周期是否受到其他异常的影响?

时间:2011-12-21 22:07:15

标签: c++ exception exception-handling

作为my previous question的后续行动:

如果我更改代码如下:

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
  }

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

void ReportError()
{
  throw SomeOtherError();
}

"最后一个处理程序"对于原始异常(即main中的那个)在抛出第二个异常时没有退出,那么两个异常都是活动的吗?一旦我们离开第二个例外的处理程序,原始异常是否仍然可用?

3 个答案:

答案 0 :(得分:2)

C ++ 11(N3242):

  

15.1p4:异常对象的内存以未指定的方式分配,除了3.7.4.1中所述。如果一个   处理程序通过重新抛出退出,控制被传递给另一个处理程序以获得相同的异常。例外   在异常的最后剩余活动处理程序以任何其他方式退出之后,对象将被销毁   而不是重新抛出,或引用异常对象的类型std::exception_ptr(18.8.5)的最后一个对象是   被摧毁,以较晚者为准。

std::exception_ptr是C ++ 11的一项功能,在您的示例代码中未使用。)

  

15.3p7:当catch子句的形式参数(如果有)的初始化完成时,处理程序被认为是活动的。 ......一个处理程序   当catch子句退出或std::unexpected()退出后,不再将其视为活动状态   因投掷而进入。

     

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

     

15.1p8:没有操作数的 throw-expression 重新抛出当前处理的异常(15.3)。

或等效地,我认为,throw;总是引用当前正在执行的最里面的catch块捕获的异常。除了我没有像标准所定义的那样严格定义'内部'和'执行',而是定义了上述所有术语。

是的,一次可以分配多个异常对象,并且需要C ++来确保它们足够长,以便在您尝试重新抛出时做“正确的事情”。

答案 1 :(得分:1)

不确定它是多么标准,但是如果我在throw;结尾之前添加HandleException并使用g ++编译,则生成的程序会告诉我:

root@xxxx [~/code]# ./a.out
terminate called after throwing an instance of 'SomeSpecificError'
  what():  17SomeSpecificError
Aborted

注意异常类型。这是MightThrow中引发的异常,而不是来自ReportError的异常。

VS2010也会报告SomeSpecificError

答案 2 :(得分:0)

一次只有一个“活动例外”。当您在异常处理程序中throw另一个异常时,实际上您正在更改正在向上传播的异常的类型。

(顺便说一下,这一切都是必要的吗?你真的觉得这段代码很容易阅读吗?)

[更新]

至于标准参考文件...... ISO / IEC 14882:2003,第15.3节[除了手柄],第8段内写着:

  

在进入处理程序时会考虑处理异常。 [注意:   堆栈将在那时解开。 ]

另一种说法是,只要您输入catch块,原始异常就不再有效。

此外,只要输入uncaught_exception()块,false功能就会返回catch。第15.5.3节[except.uncaught]读取:

  

功能

     

bool uncaught_exception() throw()

     

在完成对要抛出的对象的评估之后返回true,直到完成   在匹配处理程序中初始化异常声明   (18.6.4)。这包括堆栈展开。如果重新抛出异常   (15.1),uncaught_exception()从rethrow返回true   直到再次捕获重新抛出的异常。

[更新2]

同样相关的是第15.3节第4段:

  

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

因此,只要处理程序通过除throw;之外的任何其他方式退出,原始异常就会被销毁。因此,如果您throw有其他异常,则退出处理程序并销毁原始异常。