RTTI:Runtime Type Identification,运行时类型识别。
C++通过以下的两个操作提供RTTI:
(1)typeid运算符,该运算符返回其表达式或类型名的实际类型。
(2)dynamic_cast运算符,安全而有效地进行向下转型(downcast)。
一 typeid
1 返回值
typeid的返回为const type_info&.。type_info是std中的一个类,负责记录与类型相关的消息。
该类的拷贝构造函数以及赋值操作符被定义为private,所以不能定义或复制该类型对象。
创建type_info对象的唯一方法,就是调用typeid操作符。
2 识别
2.1 识别静态类型
当typeid的操作数如下之一时,得到操作数的静态类型(编译时的类型)。
(1)类型名
(2)一个基本类型的变量
(3)一个具体的对象
(4)一个指向不含有virtual函数的类对象的指针的解引用
(5)一个指向不含有virtual函数的类对象的引用
2.2 识别多态类型
当typeid的操作数如下之一时,运行时动态识别。
(1)一个指向含有virtual函数的类对象的指针的解引用
(2)一个指向含有virtual函数的类对象的引用
3 举例
class A {
};
class base {
public:
virtual void test() {
cout << "base test" << endl;
}
virtual ~base() {}
};
class derived1 : public base {
virtual void test() {
cout << "derived1 test" << endl;
}
};
void print(const base* p) {
cout << "typeid(p) " << typeid(p).name() << endl;
cout << "typeid(*p) " << typeid(*p).name() << endl;
}
int main() {
// 类型名
cout << "typeid(int) " << typeid(int).name() << endl;
cout << "typeid(A) " << typeid(A).name() << endl;
cout << endl;
// 具体的对象
int i;
A a;
cout << "typeid(i) " << typeid(i).name() << endl;
cout << "typeid(a) " << typeid(a).name() << endl;
cout << endl;
// 不含虚函数类的指针、解引用、引用
A* pa = &a;
cout << "typeid(pa) " << typeid(pa).name() << endl;
cout << "typeid(*pa) " << typeid(*pa).name() << endl;
cout << "typeid(&a) " << typeid(&a).name() << endl;
cout << endl;
// 含虚函数的指针、解引用
base b;
derived1 d1;
print(&b);
print(&d1);
}
结果如下:
二 dynamic_cast
把一个派生类的指针或引用转换成其基类的指针或引用总是安全的,因为派生类的对象中必然存在基类的子对象,所以通过基类的指针或引用对派生类对象进行的所有基类的操作都是合法和安全的。而向下转型有潜在的危险性,因为基类的指针可以指向基类对象或其任何派生类的对象,而该对象并不一定是向下转型的类型的对象。所以向下转型遏制了类型系统的作用,转换后对指针或引用的使用可能会引发错误的解释或腐蚀程序内存等错误。当不能转换时,dynamic_cast返回NULL。
三 type_info和虚函数表的关系
无论是typeid还是dynamic_cast,均与type_info类相关。看到两种观点,一种是type_info在虚函数表的-1位置,一种是在虚函数表的结尾。初步尝试通过虚函数表指针拿type_info未成功,暂无法验证。