【问题标题】:how c++ implements runtime type info [closed]c++ 如何实现运行时类型信息
【发布时间】:2020-11-02 12:55:03
【问题描述】:

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

【问题讨论】:

  • 您总是可以在机器代码中存储一些额外的数据,这些数据可以包含有关类型的信息,您可以通过某种方式检索这些信息
  • 您认为您需要“理解”其中的哪些内容?我对此知之甚少,然而,不知何故,我已经能够用 C++ 做很多事情,很多很多年了,尽管如此?
  • c++ 标准对 RTTI 的实现方式几乎没有说明(如果有的话),通常实现将类型信息存储在对象的 vtable 中的某处
  • @AlanBirtles 在我看来,所谓的运行时信息根本就不是运行时行为,因为在机器码层面没有类型的概念。运行时类型信息应该是编译器的行为。编译器在编译代码时,会在特定语句中分析对象的真实类型,然后根据其真实类型指定它实际调用的代码,比如多态。所以这个指定要在编译后确定,运行时不涉及类型信息。不知道这样理解对不对?

标签: c++ rtti


【解决方案1】:

有两种情况。

如果被检查的值具有多态类型,例如,编译器可以生成以某种方式将对象的 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:typeinfo for Static”)。这意味着编译器在编译时已经解析了typeid 调用。

对比动态版:

char const* get_typeid<Dynamic>(Dynamic const&):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], 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]

【讨论】:

  • @Botjie 在我看来,所谓的运行时信息根本不是运行时行为,因为在机器码层面没有类型的概念。运行时类型信息应该是编译器的行为。编译器在编译代码时,会在特定语句中分析对象的真实类型,然后根据其真实类型指定它实际调用的代码,比如多态。所以这个指定要在编译后确定,运行时不涉及类型信息。不知道这样理解对不对?
  • @Botijie 为什么叫runtime,真是误会了。我不知道它是怎么出现的。
  • @Botijie 你觉得我的理解对吗?你觉得我的理解正确吗?
  • 它被称为 runtime 类型信息,因为它允许程序在运行时恢复类型信息(通过typeiddynamic_cast),而编译器会丢弃所有编译为机器码时的类型信息。编译器必须插入额外的代码来实现这一点是一个实现细节。将 C++ 与 Java 进行比较:在 Java 中,每个对象都有一个与之关联的运行时 Class 对象,而在 C++ 中,尽可能多地在编译时解析,只留下多态对象及其类型的链接。
  • @Botijie 因此,为了提供运行时信息行为,编译器实际上会在生成的机器码中添加一个存储类型信息的表的数据结构。在运行时查询类型信息时,实际上是一个查表的过程。我的理解正确吗?
猜你喜欢
  • 2010-11-27
  • 2016-08-30
  • 1970-01-01
  • 1970-01-01
  • 2015-03-21
  • 2010-10-20
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多