首页 > 技术文章 > C++运行时动态类型

biterror 2017-05-26 17:19 原文

RTTI

运行时类型识别(RTTI)的引入有三个作用:

  1. 配合typeid操作符的实现;
  2. 实现异常处理中catch的匹配过程;
  3. 实现动态类型转换dynamic_cast

typeid操作符的实现

静态类型

C++中支持使用typeid关键字获取对象类型信息,它的返回值类型是const std::type_info&,例

#include <typeinfo>
#include <cassert>
struct B {} b, c;
struct D : B {} d;
void test() {
    const std::type_info& tb = typeid(b); 
    const std::type_info& tc = typeid(c); 
    const std::type_info& td = typeid(d);
    assert(tb == tc);   // b和c具有相同的类型
    assert(&tb == &tc); // tb和tc引用的是相同的对象
    assert(tb != td);   // 虽然D是B的子类,但是b和d的类型却不同
    assert(&tb != &td); // tb和td引用的是不同的对象
}

动态类型

当typeid的操作数引用的是一个动态类(含有虚函数的类) 类型时,它的返回值是被引用对象对应类型的const std::type_info&,例:

#include <typeinfo>
#include <cassert>
struct B { virtual void foo() {} };
struct C { virtual void bar() {} };
struct D : B, C {};
void test() {
    D d;
    B& rb = d;
    C& rc = d;
    assert(typeid(rb) == typeid(d));  // rb引用的类型与d相同
    assert(typeid(rb) == typeid(rc)); // rb引用的类型与rc引用的类型相同
}

编译时可能还不知道rb或rc引用的类型,运行时怎么能判断该返回基类还是派生类对应的类型信息对象呢?

首先我们看看虚表的结构,在虚函数指针前面有RTTI信息。

image

如果是含有虚函数,运行时便可以通过vptr找到“虚函数表”,而“虚函数表”之前的一个位置存放了需要的类型信息对象,typeid可以直接返回这里的类型信息对象引用即可。

image

实现异常处理中catch的匹配过程

catch的匹配过程也可利用与typeid相似的原理进行类型匹配判断,此不再赘述。

动态类型转换(dynamic_cast)

先上一个例子:

image

type_info对象里面会有整个继承体系的信息(通过指针),因此继承关系可以通过此树状结构判断,有了继承关系,再递归从虚表中查找基类子对象在派生类中的偏移值,便可以确定最终返回地址。

image

  • '# 4例子:
    通过pa找到_D的type_info,对比和C的type_info的不同,但是在type_info的指针里面找到C的type_info。加上C的type_info的通过偏移量,返回指针结果
  • '# 2例子:
    通过pa找到D的type_info,和目标D的type_info相同,直接加上偏移量,返回指针

参考文献

C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理

推荐阅读