第17章 RTTI和类型转换操作符

    运行时类型识别(RunTime Type Identification,RTTI),它是C++中相对较新的特性,一些老式的编译器可能不支持,不同编译器的实现方法也不尽相同。

    本章主要涉及以下知识点。

    ❑RTTI机制:介绍关于程序运行时类型识别的相关知识。

    ❑类型转换操作符:介绍常用的几种类型转换操作符。

    17.1 RTTI机制

    C++是一种静态类型语言。其数据类型是在编译期就确定的,不能在运行时更改。在第11章中讲述了多态的概念,虚函数的使用使得动态联编成为可能。例如存在一个类层次结构A->B->C,结构定义如下所示。


    class A{……};//包含虚函数 class B:public A{……}; class C:public B{……};

    此时,可以直接用派生类(B类或C类)对象为A类指针赋值,而且,通过该A类指针调用虚成员函数时,调用的版本是为其赋值的派生类对此虚函数的覆盖定义。当然,不可能把所有函数都定义成虚函数,对派生类中定义的普通函数来说,使用A类指针调用该函数是否合法需要具体分析。

    先来看如下的强制类型转换机制。


    A*pa=new B; Bpb=(B)pa;//直接赋值编译器会报错,需要强制转换上述代码是很安全的,因为pa指向的堆空间中存储的恰好为B类对象,此时使用pb调用B类中定义的非虚函数不会出错,另一个转换如下所示。 A*pa=new A; Bpb=(B)pa;

    虽然编译器不会报错,但上述代码明显不安全并存在问题,此时使用pb调用B类中调用的非虚函数必然会出错。

    注意

    虚函数调用只取决于是什么类型的变量为指针赋值,而与指针类型无关,因此不涉及类型判断问题。

    17.1.1 dynamic_cast操作符

    使用派生类对象为基类指针赋值是安全的,可什么时候使用基类指针为派生类指针赋值是安全的呢?C++提供了操作符dynamic_cast,其语法如下所示。


    Tsonp1=dynamic_cast<Tson>(pbase);

    其中pbase是基类指针,Tson是派生类型,如果pbase指向的对象是Tson型或Tson的派生类型,指针转换成功,否则p1为null,即空指针,如示例代码17.1所示。

    代码17.1 dynamic_cast的使用DynamicCastSample


    <—————————————-文件名:example1701.cpp—————————————-> 01 #include<iostream> 02 using namespace std; 03 class A//基类定义 04 { 05 public: 06 virtual void disp()//虚函数 07 { 08 cout<<"disp()in A"<<endl; 09 } 10 void printA()//普通成员函数 11 { 12 cout<<"print()in A"<<endl; 13 }; 14 }; 15 class B:public A//派生类 16 { 17 public: 18 void disp()//虚函数覆盖 19 { 20 cout<<"disp()in B"<<endl; 21 } 22 void printB()//普通成员函数 23 { 24 cout<<"print()in B"<<endl; 25 } 26 }; 27 int main() 28 { 29 A*pa=new B; 30 BpB=dynamic_cast<B>(pa);//转换是安全的 31 if(pB!=NULL) 32 { 33 pB->printB(); 34 } 35 else 36 cout<<"转换不安全,退出"<<endl; 37 A*p1=new A; 38 Bp2=dynamic_cast<B>(p1);//转换并不安全 39 if(p2!=NULL) 40 { 41 p2->printB(); 42 } 43 else 44 cout<<"转换不安全,退出"<<endl; 45 return 0; 46 }

    输出结果如下所示。


    print()in B 转换不安全,退出

    注意

    虽然VC6编译器支持RTTI,但默认情况下,该特性是关闭的,因此,在编译代码17.1时,需要将该特性打开,否则程序虽可通过编译,但运行中会报错。打开方式为:单击“Project”菜单,执行“Setting”命令或直接按快捷键“Alt+F7”,在弹出的“Project Setting”对话框中,选择C/C++页,单击“Enable Runtime Type Information”前的复选框启动该特性。

    【代码解析】代码第30和38行演示了dynamic_cast操作符的使用,在使用基类指针为派生类指针赋值时,编译器会根据基类指针实际指向的对象来决定转换是否安全,如果转换不安全,则停止该转换,将指针置为NULL。相比之下,普通的强制类型转换不会进行安全性检查,容易出现运行错误。

    dynamic_cast也可用于引用,由于没有与空指针对应的引用,因此,当转换不安全时,dynamic_cast将抛出bad_cast异常,该异常是从exception类派生而来的,定义在头文件typeinfo中,使用格式如下所示。


    #include<typeinfo>//包含要用的头文件 …… try//try块用以标记可能抛出异常的区域 { Tson&rs=dynamic_cast<Tson&>(rbase);//引用的转换 …… } catch(bad_cast&)//异常捕获机制 { …… }

    其中,Tson是派生类型,rbase是基类引用,当基类引用的对象是Tson类型或Tson类的派生类时转换成功,否则转换失败,抛出异常。