RTTI(Run-Time Type Identification)
运行时类型检查,在 C++层面主要体现在 dynamic_cast 和 typeid,VS 中虚函数表的-1 位置存放了指向 type_info 的指针。对于存在虚函数的类型,typeid 和dynamic_cast都会去查询 type_info
通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
RTTI提供了以下两个非常有用的操作符:
(1)typeid操作符,返回指针和引用所指的实际类型。
(2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
typeid操作符
该操作符的主要作用就是让用户知道当前的变量是什么类型的,比如使用typeid(a).name()就能知道变量a是什么类型的。因为typeid()操作符是一个返回类型为const typeid_info&类型的操作符,所以下面先对type_info类作下介绍
type_info类
该类的具体实现方式依编译器而定,但一般都有如下的成员定义
1 2 3 4 5 6 7 8 9 10 11 12 |
|
typeid函数使用方式
使用type_info类中的name()成员函数返回对象的类型的名称。其方法为:typeid(object).name()其中object是要显示其相应类型名的对象,该函数返回的名字因编译器而定。这里要注意的就是使用方式一中提到的虚函数类型的问题,即如果有类A,且有虚函数,类B,C,D都是从类A派生的,且都重定义了类A中的虚函数,这时有类A的指针p,再把对象类B的对象的地址赋给指针p,则typeid(p).name()将返回的类型将是A*,因为这里的p表示的是一个指针,该指针是类型为A的指针,所以返回A*,而typeid(*p).name()将返回B,因为指针p是指向类B的对象的,而*p就表示的是类B的对象,所以返回B。
dynamic_cast强制转换运算符
该转换符用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用,注意dynamic_cast转换符只能用于含有虚函数的类,其表达式为dynamic_cast<类型>(表达式),其中的类型是指要将表达式转换成的目标类型,比如含有虚函数的基类B和从基类B派生出的派生类D,则B *pb; D *pd, md; pb=&md; pd=dynamic_cast<D*>(pb); 最后一条语句表示把指向派生类D的基类指针pb转换为派生类D的指针,然后将这个指针赋给派生类D的指针pd,有人可能会觉得这样做没有意义,既然指针pd要指向派生类为什么不pd=&md;这样做更直接呢?有些时候我们需要强制转换,比如如果指向派生类的基类指针B想访问派生类D中的除虚函数之外的成员时就需要把该指针转换为指向派生类D的指针,以达到访问派生类D中特有的成员的目的,比如派生类D中含有特有的成员函数g(),这时可以这样来访问该成员dynamic_cast<D*>(pb)->g();因为dynamic_cast转换后的结果是一个指向派生类的指针,所以可以这样访问派生类中特有的成员。但是该语句不影响原来的指针的类型,即基类指针pb仍然是指向基类B的。如果单独使用该指针仍然不能访问派生类中特有的成员。一般情况下不推荐这样使用dynamic_cast转换符,因为dynamic_cast的转换并不会总是成功的,具体情况在后面介绍。
dynamic_cast的注意事项
dynamic_cast转换符只能用于指针或者引用。dynamic_cast转换符只能用于含有虚函数的类。dynamic_cast转换操作符在执行类型转换时首先将检查能否成功转换,如果能成功转换则转换之,如果转换失败,如果是指针则返回一个0值,如果是转换的是引用,则抛出一个bad_cast异常,所以在使用dynamic_cast转换之间应使用if语句对其转换成功与否进行测试,比如pd=dynamic_cast<D*>(pb); if(pd){…}else{…},或者这样测试if(dynamic_cast<D*>(pb)){…}else{…}。