这一部分将讨论C++中两个支持”现代 面向对象程序设计“的特征。运行时类型识别(Run-Time Type Identification,RTTI)和强制转换运算符。C++的初始定义并没有包含这两个特征,增加他们是为了增强C++对运行时多态的支持。RTTI允许应用程序在执行期间标识一个对象的类型。而强制转换运算符可以给你带来更安全的、更加可控的类型转换方法。你将会看到强制转换运算符之一dynamic_cast是与RTTI直接相关的。所以将”强制转换运算符“和RTTI放在一起讨论。
对你来说,运行时的类型识别可能是个新东西,因为在非多态语言(例如:C)中找不到这个概念的。非多态语言不需要运行时的类型信息,因为每个对象的类型在编译时就已经确定了(例如:在写程序的时候我们指定了对象的类型)。但是在支持多态的语言中(例如C++),可能存在这种情况:在编译时你并不知道某个对象的类型信息,而只有在程序运行时才能获得对象的准确信息。你已经知道,C++是通过类的层次结构、虚函数以及基类指针来实现多态的。基类指针可以用来指向基类的对象或者其派生类的对象,也就是说,我们并不总是能够在任何时刻都预先知道基类指针所指向对象的实际类型。因此,必须在程序中使用“运行时类型识别”来识别对象的实际类型。
我们可以通过typeid来获得一个对象的类型,在使用typeid之前,你必须在程序中包含头文件<typeinfo.h>。typeid的最常见使用形式如下:typeid(object).其中,object是你想获得“类型信息”的对象。这个对象可以是任意的类型,包括C++的内置类型以及你创建的类型,typeid将返回一个type_info 类型的对象引用 来描述object的类型信息。在type_info中定义了下面这些公有成员:
bool operator==(const type_info &ob);
bool operator!=(const type_info &ob);
bool before(const type_info &ob);
const char* name();
其中重载的运算符==和!=可以用来比较类型信息,如果在类型信息的列表中"调用before的对象类型"在"对象ob的类型"之前,那么函数before将返回 真,否则返回假(这个函数通常在内部使用,函数的返回值与“继承”或者“类的层次结构”是没有关系的)。函数name()将返回一个指向类型名字的指针:下面是使用typeid的示例程序:
- #include <iostream.h>
- #include <typeinfo.h>
- class myClass
- {};
- void main()
- {
- cout<<"Start"<<endl;
- int i,j;
- float f;
- myClass ob;
- cout<<"The type of i is:"<<typeid(i).name()<<endl;
- cout<<"The type of f is:"<<typeid(f).name()<<endl;
- cout<<"The type of i ob:"<<typeid(ob).name()<<endl;
- if(typeid(i)==typeid(j))
- cout<<"The type i and j are the same"<<endl;
- if(typeid(i)!=typeid(f))
- cout<<"The type i and f are the same"<<endl;
- cout<<"End"<<endl;
- }
- //输出结果
- Start
- The type of i is:int
- The type of f is:float
- The type of i ob:class myClass
- The type i and j are the same
- The type i and f are the same
- End
我们可以将typeid应用于多态基类(多态类是包含虚函数的类)指针。这也可能是typeid最重要的用途。在这种情况中,typeid将自动的返回指针所指向对象的实际类型,返回值可能是基类对象或者从基类中派生类的对象().因此,通过使用typeid你可以在运行时确定基类指针指向对象的实际类型。下面的程序说明了这种用法:
- #include <iostream.h>
- #include <typeinfo.h>
- class base
- {
- virtual void f(){}//基类是多态类
- };
- class Derived1 : public base
- {};
- class Derived2 : public base
- {};
- void main()
- {
- cout<<"Start"<<endl;
- base *p,baseob;
- Derived1 ob1;
- Derived2 ob2;
- p=&baseob;
- cout<<"p is pointing to object of type is:"<<typeid(*p).name()<<endl;
- p=&ob1;
- cout<<"p is pointing to object of type is:"<<typeid(*p).name()<<endl;
- p=&ob2;
- cout<<"p is pointing to object of type is:"<<typeid(*p).name()<<endl;
- cout<<"End"<<endl;
- }
- //输出结果
- p is pointing to object of type is:base
- p is pointing to object of type is:Derived1
- p is pointing to object of type is:Derived2
在程序中,当我们对“多态类型的基类指针”使用typeid时,就可以在运行时确定指针指向对象的实际类型,并输出这个类型的名字。如果typeid被应用于非多态指针类,那么我们得到的将是指针被声明的类型。也就是说,此时typeid并不会返回指针所指向对象的实际类型。我们可以验证这种情况,将虚函数f()从类base中注释掉并观察修改后程序的输出结果,你将看到,程序输出的每个对象的类型将变成base,因为指针p的类型是base。
多态类对象“引用”的用法类似于对象指针,当typeid应用于“多态类对象的引用"时,他将返回引用指向对象的实际类型,可能是基类,也可能是派生类型。当对象引用被作为参数传递给函数时,我们需要经常使用typeid来获得对象的实际类型。例如:下面程序中的函数WhatType()声明了一个base类型的引用参数。这意味着可以将”base类或者base类的派生类“的对象引用作为参数传递给函数。当对这个参数使用typeid是,我们就可以得到传递给函数的对象的实际类型。
- #include <iostream.h>
- #include <typeinfo.h>
- class base
- {
- virtual void f(){}//基类是多态类
- };
- class Derived1 : public base
- {};