对于带虚函数的类,在运行时执行RTTI操作符,但对于其他类型,在编译时计算RTTI操作符。
当具有基类的引用或指针,但需要执行不是基类组成部分的派生类操作的时候,需要动态的强制类型转换。通常,从基类指针获得派生类行为最好的方法是通过虚函数。当使用虚函数的时候,编译器会自动根据对象的实际类型选择正确的函数。
但是,在某些情况下,不可能使用虚函数,此时RTTI提供了可选的机制。值得小心的是,这种机制要比使用虚函数更容易出错。
下面介绍的是提供RTTI的两个操作符:
1.dynamic_cast操作符:可以使用它操作符将基类类型对象的引用或指针转换为同一继承层次中其他类型的引用或指针。与dynamic_cast一起使用的指针必须是有效的(即为0或者指向一个对象)。
与其他强制类型转换不同,dynamic_cast涉及到了运行时类型检查。如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast失败。若转换到指针的类型失败,则dynamic_cast的结果为0值;若转换到引用的类型失败,则抛出一个bad_cast类型的异常。
因此,dynamic_cast操作符一次执行两个操作。它首先验证被请求的转换是否有效,只有转换有效,操作符才实际进行转换。一般而言,引用或指针所绑定的对象的类型在编译时是未知的,基类的指针可以赋值为指向派生类对象,同样,基类的引用也可以用派生类对象初始化,因此,dynamic_cast操作符执行的验证必须在运行时进行。
dynamic_cast的使用:
使用基类类型对象的指针转化为同一继承层次中其他类型的指针:假设Base是至少带一个虚函数的类,并且Derived类派生于Base,有一个名为basePtr指向Base的指针,就可以将其转换位指向Derived类型的指针:
if (Derived *derivedPtr = dynamic_cast<Derived*>(basePtr)) {
...
} else {
...
}
当if中的条件失败时,derivedPtr将被置为0。(可以对值为0的指针应用dynamic_cast,其结果仍为0。)使用dynamic_cast将基类引用转换为派生类引用,这种dynamic_cast操作的形式如下:
dynamic_cast< Type& >(val)
其中type是转换的目标类型,而val是基类类型的对象。
只有当val实际引用一个Type类型对象,或者val是一个Type派生类型的对象的时候,dynamic_cast操作才将操作数val转换为想要的Type&类型。
因为不存在空引用,所以不可能对引用使用用于指针强制类型转换的检查策略,相反,当转换失败的时候,它抛出一个std::bad_cast异常,该异常在库文件typeinfo中定义。
为RTTI提供的第二个操作符是typeid操作符。typeid操作符使程序能够问一个表达式:
tyeid(e)
这里e是任意表达式或者类型名。
如果表达式的类型是类类型且该类包含一个或多个虚函数,则表达式的动态类型可能不同于它的静态编译时类型。例如,如果表达式对基类指针解引用,则该表达式对基类指针解引用,则该表达式的静态编译时类型是基类类型;但是,如果指针实际指向派生类对象,则typeid操作符将说表达式的类型时派生类型。
typeid操作符可以与恩赫类型的表达式一起用。内置类型的表达式以及常量都可以用做typeid操作符的操作数。如果操作数不是类类型或者是没有虚函数的类,则typeid操作符指出操作数的静态类型;如果操作数定义了至少一个虚函数的类类型,那么就在运行时计算类型。
由于typeid操作符的结果是type_info的标准库类型的对象引用,因此在使用type_info时,必须要包含头文件typeinfo。(type_info的默认构造函数和复制构造函数以及赋值操作符都是private,因此不能定义或者复制type_info类型的对象,也就是说type_info的唯一使用方法就是使用typeid操作符。)
typeid的使用:
最常见的使用方式就是比较两个表达式的类型,或者将表达式的类型与特定类型相比较:
Base *bp;
Derived *dp;
if (typeid(*bp) == typeid(*dp)) {
...
}
if (typeid(*bp) == typeid(Derived)) {
...
}
第一个if中,比较bp所指对象与dp所致对象的实际类型,同理,第二个if中bp当前指向的对象与Derived做比较。(typeid的操作数是表示对象的表达式)注意:只有当typeid的操作数是带虚函数类类型的对象时,才返回动态类型信息。测试指针(即指针指向的对象)返回指针静态的编译时类型。