如何实现C++异常处理运行时?

Pau*_*eph 79 c++ error-handling language-implementation exception

我对C++异常处理机制的工作方式很感兴趣.具体来说,存储异常对象的位置以及它如何通过多个范围传播直到被捕获?它存储在一些全球范围内吗?

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

MSa*_*ers 43

实现可能会有所不同,但有一些基本的想法可以从需求中得出.

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

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

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

  • AFAIR g ++使用第二种地址表方法,大概是出于与C兼容的原因.Microsoft C++编译器使用组合方法,因为它的C++异常建立在SEH之上(结构化异常处理).在每个C++函数中,MSC++创建并注册SEH异常处理记录,该记录指向具有此特定函数中的try-catch块和析构函数的地址范围的表.throw将C++异常打包为SEH异常并调用RaiseException(),然后SEH将控制权返回给特定于C++的处理程序例程. (4认同)
  • @speedplane:不,这更多是由于缺乏理解。错误处理从来都不是免费的。C只是强迫你自己写。我们都知道有多少 C 程序在一些很少使用的代码路径中缺少 `free()` 或 `fclose()`。 (2认同)

Mar*_*ork 18

这是在15.1中定义的.抛出标准的例外.

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

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

除非重新抛出异常,否则临时在被捕获的处理程序的末尾被销毁.

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

来自S.Meyers的建议(Catch by const reference).

try
{
    // do stuff
}
catch(MyException const& x)
{
}
catch(std::exception const& x)
{
}
Run Code Online (Sandbox Code Playgroud)

  • 其他未指定的是*如何*程序展开堆栈以及*如何*程序知道“最近的处理程序”在哪里。我很确定 Borland 拥有一种实现方法的专利。 (3认同)
  • 被否决: a) “Scott Meyers”,而不是“S. Myers”;b) 不真实的引用:“Effective C++”:“第 13 项:通过 **引用捕获异常。**”。这将允许调整/附加信息到异常对象。 (3认同)
  • @phresnel:不要忘记第21项:"尽可能使用const".调整异常没有好的理由.你应该a)"修复和丢弃",b)重新投掷或c)产生新的例外. (3认同)

Edu*_*anu 12

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

查看普通C中使用的技巧来实现一些基本的异常处理也可能有所帮助.这需要以下列方式使用setjmp()和longjmp():前者保存堆栈以标记异常处理程序(如"catch"),而后者用于"抛出"值."抛出"值被视为已从被调用函数返回.当再次调用setjmp()或函数返回时,"try block"结束.


Jul*_*May 8

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