89

我对 C++ 异常处理机制的工作原理很感兴趣。具体来说,异常对象存储在哪里以及它如何通过多个范围传播直到被捕获?它是否存储在某个全球区域?

由于这可能是特定于编译器的,有人可以在 g++ 编译器套件的上下文中解释这一点吗?

4

4 回答 4

54

实现可能会有所不同,但有一些基本的想法来自需求。

异常对象本身是在一个函数中创建的对象,在其调用者中销毁。因此,在堆栈上创建对象通常是不可行的。另一方面,许多异常对象并不是很大。因此,如果实际需要更大的异常对象,可以创建例如 32 字节缓冲区并溢出到堆。

至于实际的控制权转移,存在两种策略。一种是在堆栈本身中记录足够的信息以展开堆栈。这基本上是要运行的析构函数和可能捕获异常的异常处理程序的列表。当异常发生时,返回执行这些析构函数的堆栈,直到找到匹配的 catch。

第二种策略将此信息移动到堆栈外的表中。现在,当发生异常时,调用堆栈用于找出进入但未退出的范围。然后在静态表中查找它们以确定抛出的异常将在哪里处理,以及在其间运行哪些析构函数。这意味着堆栈上的异常开销更少;无论如何都需要返回地址。这些表是额外的数据,但编译器可以将它们放在程序的按需加载段中。

于 2009-01-29T10:19:22.627 回答
21

这在 15.1 抛出标准异常中定义。

throw 创建一个临时对象。
未指定如何分配此临时对象的内存。

在创建临时对象后,控件被传递给调用堆栈中最近的处理程序。在 throw 和 catch 点之间展开堆栈。随着堆栈展开,任何堆栈变量都以与创建相反的顺序被销毁。

除非重新抛出异常,否则临时文件会在处理程序结束时被销毁。

注意:如果通过引用捕获,则引用将引用临时对象,如果按值捕获,则将临时对象复制到值中(因此需要复制构造函数)。

来自 S.Meyers 的建议(通过 const 引用获取)。

try
{
    // do stuff
}
catch(MyException const& x)
{
}
catch(std::exception const& x)
{
}
于 2009-01-29T08:28:28.877 回答
13

您可以在这里查看详细说明。

看看普通 C 语言中用于实现某种基本异常处理的技巧也可能会有所帮助。这需要以下列方式使用 setjmp() 和 longjmp():前者保存堆栈以标记异常处理程序(如“catch”),而后者用于“抛出”一个值。“抛出的”值被看作是从被调用函数返回的。当再次调用 setjmp() 或函数返回时,“try 块”结束。

于 2009-01-29T07:57:44.727 回答
10

我知道这是一个老问题,但是有一个很好的阐述,在这里解释了 gcc 和 VC 中使用的方法:http ://www.hexblog.com/wp-content/uploads/2012/06/Recon- 2012-Skochinsky-Compiler-Internals.pdf

于 2012-08-22T08:29:12.073 回答