C ++如何实现运行时类型信息

问题描述

我想知道C ++语言如何在运行时保存变量的类型信息。在我看来,运行时意味着将程序员编写的源代码文件编译为与特定机器相关的二进制格式的机器代码。该二进制文件全部由机器指令和操作数组成。在机器级别上,没有这样的东西。类型的概念只是存储在寄存器和存储器中的值。从机器的角度来看,这些值没有上层抽象类型。那么,如何在运行时(机器代码执行的过程)存储这些类型信息?我应该如何理解运行时类型信息的声明?

解决方法

有两种情况。

如果要检查的值具有多态类型,则编译器可以例如生成以某种方式将对象的vtable映射到静态类型信息块的代码。该块甚至可能在vtable中!

如果要检查的值具有已知的静态类型,则编译器可以直接直接插入指向该对象的类型信息的指针。

您所得到的答案会因编译器而异,但是我对使用GCC编译的简单程序进行了一些研究。 例如,给定以下定义:

template <class T>
const char* get_typeid(const T& val) {
    return typeid(val).name();
}

class Static{};
class Dynamic {
    public: virtual ~Dynamic() = default; // Making the class polymorphic
};

编译器可以为静态生成以下代码:

char const* get_typeid<Static>(Static const&):
        push    rbp
        mov     rbp,rsp
        sub     rsp,16
        mov     QWORD PTR [rbp-8],rdi
        mov     edi,OFFSET FLAT:typeinfo for Static
        call    std::type_info::name() const
        leave
        ret

请注意它如何访问全局常量(“ OFFSET FLAT:static的typeinfo”)。这意味着编译器已在编译时解决了typeid调用。

与动态版本比较:

char const* get_typeid<Dynamic>(Dynamic const&):
        push    rbp
        mov     rbp,rdi
        mov     rax,QWORD PTR [rbp-8] # rax = pointer to Dynamic
        mov     rax,QWORD PTR [rax] # rax = vtable pointer of Dynamic
        sub     rax,8
        mov     rax,QWORD PTR [rax] #rax = typeinfo field of vtable
        mov     rdi,rax
        call    std::type_info::name() const
        leave
        ret

请注意,它如何通过获取传递的Dynamic对象的vtable并减去8来访问类型信息。为完整起见,以下是Dynamic vtable的定义:

vtable for Dynamic:
        .quad   0
        .quad   typeinfo for Dynamic
        .quad   Dynamic::~Dynamic() [complete object destructor] # this is where the vtable normally points to.
        .quad   Dynamic::~Dynamic() [deleting destructor]