C++ RTTI(Run-Time Type Indentifiation,运行时刻类型识别)
RTTI 运行时刻类型识别允许用指向基类的指针或引用来操纵对象的程序能够获
取到这些指针或引用所指对象的实际派生类型在c++中为了支持RTTI 提供了两个
操作符dynamic_cast和typeid
1 dynamic_cast 操作符可以用来把一个类类型对象的指针转换成同一类层次结构中的其他
类的指针同时也可以用它把一个类类型对象的左值转换成同一类层次结构中其他类的引用.
1.1 当dynamic_cast操作符的操作数是带有虚拟函数的类类型指针和左值时
dynamic_cast 是在运行时刻执行的.
并且如果指针或左值操作数不能被转换成目标类型,
dynamic_cast在运行时将失败.
针对指针类型的dynamic_cast失败,dynamic_cast 的结果是0
针对引用类型的dynamic_cast失败,dynamic_cast 的结果是抛出一个异常.
void company::payroll( employee *pe )
{
programmer *pm = dynamic_cast< programmer* >( pe );
if ( pm ) {
// dynamic_cast 成功使用 programmer 的成员函数
} else {
// dynamic_cast 失败使用 employee 的成员函数
}
}
void company::payroll( employee &re )
{
try {
programmer &rm = dynamic_cast< programmer & >( re );
// 用 rm 调用 programmer::bonus()
} catch ( std::bad_cast ) {
// 使用 employee 的成员函数
}
}
1.2 当dynamic_cast操作符的操作数不是是带有虚拟函数的类类型指针和左值时
dynamic_cast 是在编译时刻执行的.
并且如果指针或左值操作数不能被转换成目标类型,编译时就将失败.
例如
class employee {
public:
employee() {};
~employee() {};
//成员函数都是非虚的
};
class programmer : public employee{
public:
programmer() {};
~programmer() {};
//成员函数都是非虚的,但有可能存在重载
};
employee eobj;
void company::payroll( employee *pe )
{
programmer *pm = dynamic_cast< programmer* >( pe );
if ( pm ) {
// dynamic_cast 成功使用 programmer 的成员函数
} else {
// dynamic_cast 失败使用 employee 的成员函数
}
}
payroll(&eobj); //编译时刻执行dynamic_cast,编译出错,
programmer pobj;
payroll(&pobj); //编译时刻执行dynamic_cast,编译OK.
2 typeid 操作符指出指针或引用指向的对象的实际派生类型
typeid 操作符必须与表达式或类型名一起使用
2.1 当typeid操作符的操作数是类类型并带有虚拟函数的类类型时
typeid 操作符会指出操作数的底层真正对象的类型
#include <type_info>
programmer pobj;
employee eobj;
employee* pe = &pobj;
employee &re = pobj;
cout << typeid(*pe).name() << endl; // 操作符指出指针指向的对象的实际派生类型 name() 返回 programmer.name()
cout << typeid(re).name() << endl; // 操作符指出引用指向的对象的实际派生类型 name() 返回 programmer.name()
2.2 当typeid操作符的操作数不是类类型时typeid 操作符会指出操作数的类型
例如内置类型的表达式和常量可以被用作typeid 的操作数
int iobj;
cout << typeid( iobj ).name() << endl; // 打印: int
cout << typeid( 8.16 ).name() << endl; // 打印: double
2.3 当typeid操作符的操作数是类类型但不是带有虚拟函数的类类型时
typeid 操作符会指出操作数的类型而不是底层对象的类型
class Base { /* 没有虚拟函数 */ };
class Derived : public Base { /* 没有虚拟函数 */ };
Derived dobj;
Base *pb = &dobj;
cout << typeid( *pb ).name() << endl; // 打印: Base
2.4 可以对typeid 的结果进行比较.
#include <type_info>
programmer pobj;
employee eobj;
employee* pe = &pobj;
if ( typeid( pe ) == typeid( employee* ) ) // true
if ( typeid( pe ) == typeid( programmer* ) ) // false
if ( typeid( *pe ) == typeid( employee ) ) // false
if ( typeid( *pe ) == typeid( programmer ) ) // true
看出true和false不同结果了吗?
前者是typeid操作符对不是类类型(指针类型)的操作.
后者是typeid操作符对类类型的操作.
3. 另外需要指出的是对于带有虚拟函数的类而言RTTI 操作符是
运行时刻的事件(如上所述)而对于其他类而言它只是编译时刻的事件
因此1.1和2.1的情况是编译时刻的转换事件
而1.1和2.2,2.3的情况是运行时刻的转换事件