在异常处理期间键入分辨率

时间:2017-05-23 17:31:46

标签: c++ exception exception-handling

当在C++中抛出异常并且堆栈被解开时,如何选择正确的处理程序(catch子句)来处理异常?

void f1()
{
    throw 1;
}

void f2()
{
    try
    {
        f1();
    }
    catch(const char* e)
    {
        std::cout << "exc1";
    }
}

...
try
{
    f2();
}
catch(int& e)
{
    std::cout << "exc2";
}
...

例如,此代码不出所料地打印"exc2",因为catch(int& e)能够处理1 int类型的对象。

我不明白的是,如何以静态方式解决这个问题?还是动态解决?类型信息是否传播了异常?

3 个答案:

答案 0 :(得分:3)

对于大多数事情,C ++标准没有指定实现,但约束有效的实现。这不是一个了解具体细节的普遍答案。

Itanium ABI是一种流行的ABI,它提供与语言无关的异常支持。在该实现中,展开API调用堆栈帧的个性函数,它接收异常上下文,异常结构和引用捕获行为的异常处理表的引用。根据程序异常表中的调用返回地址查找个性函数;假设可以发起异常的唯一指令是调用指令。 (GCC具有允许通过启用&#34;非调用异常&#34;来从信号处理程序抛出的扩展。)使用Itanium ABI的编译器提供了一个知道如何检查异常对象的运行时类型的个性函数,以及它们将该类型与异常表的元素进行比较。

还有其他方法可以做到这一点。例如,在32位Windows上,异常处理通过在堆栈上设置处理程序函数作为链接列表项来工作。这些链接列表节点包含异常处理程序的地址,它们使用EXCEPTION_RECORD来确定从那里开始的位置。不幸的是,我自己的细节有点稀疏。

顺便说一句,Itanium ABI并不是C ++独有的。例如,在Apple平台上,Objective-C代码也使用Itanium ABI作为例外。这有一个有趣的特性,即任何一种语言的catch-all子句都会捕获另一种语言的异常。

答案 1 :(得分:1)

你可以说&#34;类型信息传播的例外情况是&#34;,但从技术上讲,这不是真的。当您graphicsContext.drawImage(img, x, y, width, heighth); 某事时,该对象被复制/移动,编译器将按照它们出现的顺序查找appropriate catch clause,其中对象将被复制/移动到:

  

如果满足以下任何条件,则例外是匹配:

     
      
  • E和T属于同一类型(忽略T上的顶级cv限定符)

  •   
  • T是左值(可能是cv认证的)E

  •   
  • T是E

  • 的明确公共基类   
  • T是对E

  • 的明确公共基类的引用   
  • T是(可能是cv认证的)U或const U&amp; (自C ++ 14开始),U是指向成员(自C ++ 17)类型的指针或指针,E也是指向成员(自C ++ 17)类型的指针或指针,可以隐式转换为U

    中的一个或多个      
        
    • 除了一个私有,受保护或不明确的基类之外的标准指针转换
    •   
    • 资格转换
    •   
    • 函数指针转换   (自C ++ 17起)
    •   
    • T是指向成员的指针或指针或对const指针的引用(自C ++ 14起),而E是throw
    •   
  •   

此外,如果存在catch-all子句(std::nullptr_t),则如果上述任何一点不适用,则采用该子句。如果没有catch (...)子句是合适的,则异常会继续向上调用堆栈。

答案 2 :(得分:0)

正如一些答案和评论所述,编译器确实为异常调度程序用于将正确处理程序与异常对象匹配的异常类型生成运行时类型信息RTTI。

正如您在我的示例的修改版本中所看到的那样:

https://godbolt.org/g/dyheBZ

编译器(gcc)在typeinfo部分为.rodatathrow语句中使用的对象生成catch块:

    .size   typeinfo for A, 16
typeinfo for A:
    .quad   vtable for __cxxabiv1::__class_type_info+16
    .quad   typeinfo name for A
    .weak   typeinfo name for A
    .section        .rodata._ZTS1A,"aG",@progbits,typeinfo name for A,comdat
    .type   typeinfo name for A, @object
    .size   typeinfo name for A, 3
typeinfo name for A:
    .string "1A"
    .text
    .type   __static_initialization_and_destruction_0(int, int), @function

throw A();语句使用此信息抛出异常:

    mov     edi, 1
    call    __cxa_allocate_exception
    mov     edx, 0
    mov     esi, OFFSET FLAT:typeinfo for A
    mov     rdi, rax
    call    __cxa_throw