RTTI(Runtime Type Identification)是“运行时类型识别”的意思。C++引入这个机制是为了让程序在运行时能根据基类的指针或引用来获得该指针或引用所指的对象的实际类型。但是现在RTTI的类型识别已经不限于此了,它还能通过typeid操作符识别出所有的基本类型的变量对应的类型。为什么会出现RTTI这一机制呢?这和C++语言本身有关系,C++是一门静态类型语言,其数据类型是在编译期就确定的,不能在运行时更改。然而由于面向对象程序设计中多态性的要求,C++中的指针或引用本身的类型,可能与它实际代表的类型并不一致,有时我们需要将一个多态指针转换为其实际指向对象的类型,就需要知道运行时的类型信息,这就有了运行时类型识别需求。和Java相比,C++要想获得运行时类型信息,只能通过RTTI机制,并且C++最终生成的代码是直接与机器相关的。
C++通过以下两个关键字提供RTTI功能:
typeid:该运算符返回其表达式或类型名的实际类型
dynamic_cast:该运算符将基类的指针或引用安全地转换为派生类类型的指针或引用(也就是所谓的下行转换)(面试过程可能会闻到如何判断基类的指针或引用指向的对象是基类还是派生类,这样就可以通过dynamic_cast转换成基类查看)
typeid识别静态类型
当typeid中的操作数是以下任意一种时,typeid得出的是静态类型,即编译时就确定的类型:
一个任意的类型名
一个基本内置类型的变量,或指向基本内置类型的指针或引用
一个任意类型的指针(指针就是指针,本身不体现多态,多指针解引用才有可能会体现多态)(如下边的typeid(p)返回基类类型。)
一个具体的对象实例,无论对应的类有没有多态都可以直接在编译器确定
一个指向没有多态的类对象的指针的解引用
一个指向没有多态的类对象的引用
由于静态类型在程序的运行过程中并不会改变,所以并不需要等到程序运行时再去推算其类型,在编译时期就能根据操作数的静态类型,从而推导出其具体类型信息。
typeid识别动态类型
当typeid中的操作数是以下任意一种时,typeid需要在程序运行时推算类型,因为其操作数的类型在编译时期是不能被确定的:
一个指向含有多态的类对象的指针的解引用
一个指向含有多态的类对象的引用
#include<iostream>
#include<typeinfo>
using namespace std;
class Base
{
public:
virtual void funcA() { cout << "Base" << endl; }
};
class Derived : public Base
{
public:
virtual void funcB() { cout << "Derived" << endl; }
};
//funcC是用dynamic_cast类型转换是否成功来识别类型的,
//dynamic_cast操作符将基类类型对象的引用或指针转化为同一继承层次中的其他类型的引用或指针。
void funcC(Base* p)
{
Derived* dp = dynamic_cast<Derived*>(p);
//如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast失败。
//如果转换到指针类型的dynamic_cast失败,则dynamic_cast的结果是NULL值;
//如果转换到引用类型的dynamic_cast失败, 则抛出一个bad_cast类型的异常
if (dp != NULL) {//说明转换成功
dp->funcB();
}
else {
p->funcA();//转换失败
}
};
//funcD是用typeid判断基类地址是否一致的办法来识别类型的。
void funcD(Base* p)
{
Derived* dp = NULL;
if (typeid(*p) == typeid(Derived))//typeid(*p)返回p所指向的派生类类型
{
dp = static_cast<Derived*>(p);//static_cast,基本数据类型之间的转换
dp->funcB();
}
else {
p->funcA();
}
}
int main(int argc, char const* argv[])
{
Base* p = new Derived;//基类的指针指向派生类
//typeid用于返回指针或引用所指对象的实际类型
//如果p是基类指针,并且指向一个派生类型的对象,并且基类中有虚函数,
//那么typeid(*p)返回p所指向的派生类类型,
//typeid(p)返回基类类型。
cout << typeid(p).name() << endl;//P4Base
cout << typeid(*p).name() << endl;//7Derived
funcD(p);//Derived
funcC(p);//Derived
delete p;
//基类的指针指向基类
Base* dp = new Base;
funcC(dp);//Base
funcD(dp);//Base
delete dp;
return 0;
}
RTTI的实现原理: 通过在虚表中放一个额外的指针,每个新类只产生一个typeinfo实例,额外指针指向typeinfo, typeid返回对它的一个引用。