C++对象模型之RTTI的实现原理

RTTI是Runtime Type Identification的缩写 , 意思是运行时类型识别 。 C++引入这个机制是为了让程序在运行时能根据基类的指针或引用来获得该指针或引用所指的对象的实际类型 。 但是现在RTTI的类型识别已经不限于此了 , 它还能通过typeid操作符识别出所有的基本类型(int , 指针等)的变量对应的类型 。
C++通过以下的两个操作提供RTTI:
(1)typeid运算符 , 该运算符返回其表达式或类型名的实际类型 。
(2)dynamic_cast运算符 , 该运算符将基类的指针或引用安全地转换为派生类类型的指针或引用 。
下面分别详细地说明这两个操作的实现方式 。
注所有的测试代码的测试环境均为:32位Ubuntu 14.04 g++ 4.8.2 , 若在不同的环境中进行测试 , 结果可能有不同 。
1、typeid运算符
typeid运算符 , 后接一个类型名或一个表达式 , 该运算符返回一个类型为std::tpeinf的对象的const引用 。 type_info是std中的一个类 , 它用于记录与类型相关的信息 。 类type_info的定义大概如下:
class type_info{public:virtual ~type_info();bool operator==(const type_infobool operator!=(const type_infobool before(const type_infoconst char* name()const;private:type_info(const type_infotype_info// data members};至于data members部分 , 不同的编译器会有所不同 , 但是都必须提供最小量的信息是class的真实名称和在type_info对象之间的某些排序算法(通过before()成员函数提供) , 以及某些形式的描述器 , 用来表示显式的类的类型和该类的任何子类型 。
从上面的定义也可以看到 , type_info提供了两个对象的相等比较操作 , 但是用户并不能自己定义一个type_info的对象 , 而只能通过typeid运算符返回一个对象的const引用来使用type_info的对象 。 因为其只声明了一个构造函数(复制构造函数)且为private , 所以编译器不会合成任何的构造函数 , 而且赋值操作运行符也为private 。 这两个操作就完全禁止了用户对type_info对象的定义和复制操作 , 用户只能通过指向type_info的对象的指针或引用来使用该类 。
下面说说 , typeid对静态类型的表达式和动态类型的表达式的处理和实现 。
1)typeid识别静态类型
当typeid中的操作数是如下情况之一时 , typeid运算符指出操作数的静态类型 , 即编译时的类型 。
(1)类型名
(2)一个基本类型的变量
(3)一个具体的对象
(4)一个指向不含有virtual函数的类对象的指针的解引用
(5)一个指向不含有virtual函数的类对象的引用
静态类型在程序的运行过程中并不会改变 , 所以并不需要在程序运行时计算类型 , 在编译时就能根据操作数的静态类型 , 推导出其类型信息 。 例如如下的代码片断 , typeid中的操作数均为静态类型:
class X{...... // 具有virtual函数 }; class XX : public X{ ...... // 具有virtual函数}; class Y{ ...... // 没有virtual函数};int main(){int n = 0;XX xx;Y y;Y *py =// int和XX都是类型名cout << typeid(int).name() << endl;cout << typeid(XX).name() << endl;// n为基本变量cout << typeid(n).name() << endl;// xx所属的类虽然存在virtual , 但是xx为一个具体的对象cout << typeid(xx).name() << endl;// py为一个指针 , 属于基本类型cout << typeid(py).name() << endl;// py指向的Y的对象 , 但是类Y不存在virtual函数cout << typeid(*py).name() << endl;return 0;}需要C/C++ Linux高级服务器架构师学习资料后台私信“资料”(包括C/C++ , Linux , golang技术 , Nginx , ZeroMQ , MySQL , Redis , fastdfs , MongoDB , ZK , 流媒体 , CDN , P2P , K8S , Docker , TCP/IP , 协程 , DPDK , ffmpeg等)
C++对象模型之RTTI的实现原理文章插图
2)typeid识别多态类型
当typeid中的操作数是如下情况之一时 , typeid运算符需要在程序运行时计算类型 , 因为其其操作数的类型在编译时期是不能被确定的 。
(1)一个指向不含有virtual函数的类对象的指针的解引用
(2)一个指向不含有virtual函数的类对象的引用
多态的类型是可以在运行过程中被改变的 , 例如 , 一个基类的指针 , 在程序运行的过程中 , 它可以指向一个基类对象 , 也可以指向该基类的派生类的对象 , 而typeid运算符需要在运行过程中识别出该基类指针所指向的对象的实际类型 , 这就需要typeid运算符在运行过程中计算其指向的对象的实际类型 。 例如对于以下的类定义:
class X{public:X(){mX = 101;}virtual void vfunc(){cout << "X::vfunc()" << endl;}private:int mX;};class XX : public X{public:XX():X(){mXX = 1001;}virtual void vfunc(){cout