运行阶段类型识别RTTI(Runtime Type Identification)
RTTI旨在为程序在运行阶段确定对象的类型提供一种标准方式。
RTTI 只适用于包含虚函数的类。
RTTI的工作原理
C++有3个支持RTTI的元素:
(1) 如果可能的话,dynameic_cast操作符将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该操作符返回0 —— 空指针。
(2) typeid操作符返回一个指出对象的类型的值。
(3) type_info结构存储了有关特定类型的信息。
只能将RTTI用于包含虚函数的类层次结构,因为只有对于这种层次结构,才应该将派生对象的地址赋给基类指针。
1. dynameic_cast 操作符
该操作符不能回答“指针指向的是哪类对象”这样的问题,但能够回答“是否可以安全地将对象的地址赋给特定类型的指针”这样的问题。
假设有如下类层次结构:
class Grand { // has virtual methods };
class Superb: public Grand { ... };
class Magnificent: public Superb { ... };
创建下面的指针:
Grand * pg = new Grand;
Grand * ps = new Superb;
Grand * pm = new Magnificent;
对于下面的类型转换:
Magnificent * p1 = (Magnificent *)pm; // #1
Magnificent * p2 = (Magnificent *)pg; // #2
Superb * p3 = (Magnificent *)pm; // #3
类型转换#2就是不安全的,因为它将基类对象的地址赋给派生类指针。因此,程序将期望基类对象有派生来的特征,而通常这是不可能的。
类型转换#3是安全的,因为公有派生确保Magnificent对象同时也是一个Superb对象(直接基类)和一个Grand对象(间接基类)。虚函数确保了类方法调用的正确性。
dynameic_cast操作符的用法如下,其中pg指向一个对象:
Superb * pm = dynameic_cast<Superb *>(pg);
这提出了这样的问题:指针pg的类型是否可被安全地转换为Superb *? 如果可以,操作符将返回对象的地址,否则返回一个空指针。
即使编译器支持RTTI,在默认情况下,它也可能关闭该特性。如果该特性被关闭,程序可能仍能够通过编译,但将出现运行阶段错误。例如,在Microsoft Visual C++ 7.1中,选择Project/proj properties(其中proj是当前使用的工程名),切换到选项卡C/C++,然后单击Language,将Enable Run-Time Type Info设置为Yes。
应尽可能使用虚函数,而只在必要时使用RTTI。
将dynameic_cast用于引用时,没有与空指针对应的引用值,因此无法使用特殊的引用值来指示失败。当请求不正确时,dynameic_cast将引发类型为bad_cast的异常,该异常是从exception类派生而来的,它是在头文件typeinfo中定义的。
可以像下面这样使用该操作符,其中rg是对Grand对象的引用:
#include <typeinfo> // for bad_cast
...
try {
Superb & rs = dynameic_cast<Superb &>(rg);
...
}
catch(bad_cast & ex) { ... }
2. typeid 操作符和 type_info 类
typeid操作符使得能够确定两个对象是否为同种类型。它与sizeof有些相像,可以接受两种参数:
(1) 类名。
(2) 结果为对象的表达式。
typeid操作符返回一个对type_info对象的引用,其中,type_info是在头文件typeinfo(以前为typeinfo.h)中定义的一个类。type_info类重载了==和!=操作符,以便可以使用这些操作符来对类型进行比较。
例如,如果pg指向的是一个Magnificent对象,则表达式:
typeid(Magnificent) == typeid(*pg)
结果将为bool值true,否则为false。
如果pg是一个空指针,程序将引发bad_typeid异常。该异常类型是从exception类派生而来的。在头文件typeinfo中声明的。
type_info类的实现随厂商而异,但包含一个name()成员,该函数返回一个字符串(内容随实现而异,但通常是类的名称)。例如:
cout <<"Now processing type " << typeid(*pg).name()<<"./n";
将显示为指针pg指向的对象所属的类定义的字符串。