应用开发

研究了一波RTTI,你会了吗

时间:2010-12-5 17:23:32  作者:系统运维   来源:应用开发  查看:  评论:0
内容摘要:最近研究了一波RTTI,整理了一下知识点,在这里分享一下,下面是目录:RTTI 是 Run Time Type Information 的缩写,从字面上来理解就是运行时期的类型信息,它的主要作用就是动

最近研究了一波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最大的目的就是为了减小代码和可执行文件的大小),所以我估计指针指向的块里面可能什么信息都没有,具体就不得而知了。

站群服务器
copyright © 2025 powered by 益强资讯全景  滇ICP备2023006006号-31sitemap