最近研究了一波RTTI,你会了吗整理了一下知识点,研究在这里分享一下,你会了吗下面是研究目录:
RTTI 是 Run Time Type Information 的缩写,从字面上来理解就是你会了吗运行时期的类型信息,它的研究主要作用就是动态判断运行时期的类型。
一般在dynamic_cast和typeid中用到,你会了吗例如父类B的研究指针转换子类A的指针,dynamic_cast会判断B究竟是你会了吗不是A的父类,如果不是研究,会返回nullptr,你会了吗相对于强转会更加安全。研究依据什么判断的你会了吗呢?就是RTTI。源码下载
先看下面这段代码:
#include <iostream> using std::cout; using std::endl; class Base { public: int a; int b; Base() { cout << this << " Base \n"; } virtual void func() { cout << this << " hello Base \n"; }; void basefunc() { cout << this << " hello basefunc \n"; } }; class BaseBB { public: int d; int c; BaseBB() { cout << this << " BaseBB \n"; } virtual void func() { cout << this << " hello BaseBB \n"; } }; class Derive : public Base { public: Derive() { cout << this << " Derive \n"; } void func() override { cout << this << " hello Derive \n"; } }; int main() { Derive *d = new Derive; typeid(d); d->func(); Base *b = static_cast<Base *>(d); b->func(); b->basefunc(); Derive *b1 = dynamic_cast<Derive *>(b); Derive *b2 = static_cast<Derive *>(b); b1->func(); b2->func(); BaseBB *b3 = dynamic_cast<BaseBB *>(b); BaseBB *b4 = reinterpret_cast<BaseBB *>(b); cout << d << " " << b << " " << b1 << " " << b2 << " " << b3 << " " << b4 << endl; return 0; }结果如下:
clang++ test_rtti.cc -std=c++11;./a.out 0x7fe80ac05920 Base 0x7fe80ac05920 Derive 0x7fe80ac05920 hello Derive 0x7fe80ac05920 hello Derive 0x7fe80ac05920 hello basefunc 0x7fe80ac05920 hello Derive 0x7fe80ac05920 hello Derive 0x7fe80ac05920 0x7fe80ac05920 0x7fe80ac05920 0x7fe80ac05920 0x0 0x7fe80ac05920上面的研究代码是正常的一段使用多态的代码,同时也包含了子类指针转基类指针,你会了吗基类指针转子类指针,从输出结果中可以看到,使用dynamic_cast进行不合理的基类子类指针转换时,会返回nullptr,而强转则不会返回nullptr,运行时肯定就会出现奇奇怪怪的错误,比较难排查。
如果在编译时加上-fno-rtti会怎么样?结果是这样:
clang++ test_rtti.cc -std=c++11 -fno-rtti test_rtti.cc:60:5: error: use of typeid requires -frtti typeid(d); ^ test_rtti.cc:65:18: error: use of dynamic_cast requires -frtti Derive *b1 = dynamic_cast<Derive *>(b); ^ test_rtti.cc:69:18: error: use of dynamic_cast requires -frtti BaseBB *b3 = dynamic_cast<BaseBB *>(b); ^ 3 errors generated.可以看到,加上了-fno-rtti编译时,使用typeid或dynamic_cast会报错,即添加-fno-rtti编译会禁止我们使用dynamic_cast和typeid。那为什么要禁止使用他们呢?
1. RTTI的空间成本非常高:每个带有vtable(至少一个虚拟方法)的类都将获得RTTI信息,其中包括类的名称及其基类的信息。此信息用于实现typeid运算符以及dynamic_cast。(大小问题大家可以自己编写代码验证一下)
2. 速度慢,云服务器提供商运行时多判断了一层,性能肯定更慢一些。
tips:我这里又将typeid和dynamic_cast去掉重新编译,结果表明添加了-fno-rtti,还是可以正常使用多态,所以大家不用担心rtti的禁用会影响多态的使用。
都知道RTTI信息是存在于虚函数表中,而添加-fno-rtti后代表禁止了RTTI,那虚函数表中还会有rtti信息吗?
我这里使用clang的命令查看一下虚函数表:
clang -Xclang -fdump-vtable-layouts -stdlib=libc++ -fno-rtti -c test_rtti.cc test_rtti.cc:51:17: warning: override keyword is a C++11 extension [-Wc++11-extensions] void func() override ^ Original map void Derive::func() -> void Base::func() Vtable for Derive (3 entries). 0 | offset_to_top (0) 1 | Derive RTTI -- (Base, 0) vtable address -- -- (Derive, 0) vtable address -- 2 | void Derive::func() VTable indices for Derive (1 entries). 0 | void Derive::func()通过结果可以看到,即使添加了-fno-rtti,虚函数表中还是会存在RTTI指针,但是我查看很多文档都说rtti会导致可执行文件的体积增大一些(毕竟-fno-rtti最大的目的就是为了减小代码和可执行文件的大小),所以我估计指针指向的块里面可能什么信息都没有,具体就不得而知了。
站群服务器