编译器如何知道要采用哪个catch块?

时间:2016-11-24 22:28:46

标签: c++ exception compiler-construction

假设我有以下两个文件main.cpp

#include <iostream>

class A {};    
void foo();

int main(void)
{
    try {
        foo();
    }
    catch(const A& e) {
        std::cout << "Caught an A." << std::endl;
    }
    return 0;
}

foo.cpp

class A {};
class B : public A {};

void foo()
{
    B b;
    throw b;
}

现在,当我单独编译这些文件时,链接生成的目标文件,并运行生成的可执行文件,我得到了预期的结果:

$ clang++ --std=c++14 -c main.cpp
$ clang++ --std=c++14 -c foo.cpp
$ clang++ --std=c++14 main.o foo.o
$ ./a.out 
Caught an A.

这令人难以置信!类A没有虚拟方法。因此,它不是多态的,并且其实例在运行时不应携带任何类型信息。 main.o目标文件不知道抛出的内容,因为实际抛出发生在foo()内,其主体在单独的编译单元中定义。 foo.o目标文件包含更多信息,但同样不知道任何catch语句和捕获的异常的预期类型。

简而言之:我没有看到两个源文件如何单独编译然后链接可以产生上述输入而不需要处理某些运行时类型信息。单独编译的文件都没有足够的信息来获取正确的catch块。

2 个答案:

答案 0 :(得分:5)

这当然完全取决于编译器。

所有编译器的约束是:

  • 抛出时会知道异常的类型(在编译时,或者在抛出多态对象的情况下,在运行时)。
  • 适用的catch块(它们可以是几个),它们的类型取决于执行路径。

这意味着必须在运行时识别类型,即使异常对象是非plymorphic。

实现此目的的一种简单方法是将指针与抛出的对象本身一起传递给typeinfo对象。这是GCC使用的方法:请参阅online code,这里是为了方便而投入foo()的摘录:

    call    __cxa_allocate_exception
    mov     edx, 0
    mov     esi, OFFSET FLAT:typeinfo for B   ; <== !! 
    mov     rdi, rax
    call    __cxa_throw

答案 1 :(得分:2)

它是 RTTI (运行时类型信息)和实现特定编码类型数据的组合,用于在尝试评估哪个catch块获取内容时比较类型。

但是简单地在面值处比较两种类型可能不会产生正确的结果(例如在基础和派生类的情况下)。如果是Windows&amp; SEH是一种特殊的附加扩展类型信息结构(etype_info或类似的东西),它包含层次结构中需要遍历以确定潜在匹配的所有类(因为基类可以是限制视图)派生类)。如果在遍历匹配后找到,则调用catch块。

<强>附录

异常处理需要OS提供的特殊运行时支持,因此只要最终结果满足标准,这种情况就会发生实现(例如Windows中的结构化异常处理)。

楼上是Windows风格的要点。