基本技能12.7:运行时的类型标识
运行时的类型信息是非多态性语言中如c或者传统的BASIC没有的一个特性,所以我们可能对此有些陌生。在这些非多态性的语言中,没有必要有运行时的类型信息。这是因为每个对象的类型在编译的时候就是确定的(也就是在编写程序的时候就是确定的)。然而,在具有多态性的语言,如C++中,可能存在编译时对象的类型不可知的情况,也就是说只有到程序运行时对象的类型才确定的情况。我们都知道,C++是通过使用类的继承关系,虚函数以及基类指针来实现多态性的。一个基类的指针可以指向基类的任何对象,也可以执行任何该基类的派生类的对象。因此,在任意指定的时刻,我们并不总是能够提前知道基类指针指向对象的实际类型。此时只有在程序运行时使用运行时类型标识才能确定基类指针指向对象的实际类型。
我们可以使用typeid()来获取一个对象的类型。此时我们必须引入头文件<typeinfo>。其常用的形式如下:
typeid(对象);
其中,对象就是要获取类型的那个对象。它可以是任意类型的,就包括了内置类型和我们自己定义的类类型。typeid返回的是对一个描述了对象类型的type_info对象的引用。(听起来很绕口!就是说返回的是对一个对象的引用。这个对象的类型是type_info类型。返回的这个对象就描述了typeid()中参数对象的类型)。
type_info类定义例如如下的共有成员:
bool operator == (consttype_info&ob);
bool operator !=(consttype_info * ob);
bool before(consttype_info&ob);
const char * name();
其中对==和!=重载是用来对类型进行比较的。before()函数是用来判断调用者是否在参数对象之前。这种前后的关系是按照对类的整理顺序来讲的。(这个函数绝大多是都是内部使用的。它的返回值和类的继承关系已经层次图没有任何关系。)name()函数返回的是指向类型名称的指针。
下面是一个使用typeid()的示例程序:
//使用typeid()函数的示例程序
#include<iostream>
#include<typeinfo>
using namespace std;
class MyClass
{
//...
};
int main()
{
int i,j;
float f;
MyClass ob;
//使用函数typeid来获取对象的运行时类型信息
cout<<"The type of i is:"<<typeid(i).name();
cout<<endl;
cout<<"The type of f is:"<<typeid(f).name();
cout<<endl;
cout<<"The type of ob is:"<<typeid(ob).name();
cout<<endl;
if(typeid(i) ==typeid(j))
{
cout<<"The types of i and j are the same/n";
}
if(typeid(i) !=typeid(f))
{
cout<<"The types of i and f are not the same/n";
}
return 0;
}
上面程序的输出如下:
The type of iis:int
The type of f is:float
The type of obis:classMyClass
The types of i and j are the same
The types of i and f are not the same
或许typeid()函数主要是用在当我们使用具有多态性的基类指针的时候。此时,该函数会自动返回指针指向对象的实际类型,一个基类类型或者是派生类类型(请记住:基类指针是可以指向任意基类的对象和任意该基类的派生类的对象的)。因此,此时使用typeid()函数我们就可以在运行时确定一个基类指针指向对象的类型。下面的程序就对此进行了演示:
//在具有多态性的层次关系中使用时typeid()函数的示例程序
#include<iostream>
#include<typeinfo>
using namespace std;
class Base
{
virtual void f()//虚函数使得该基类具有多态性
{
}
//...
};
class Derived1:publicBase
{
//...
};
class Derived2:publicBase
{
//...
};
int main()
{
Base *p,baseob;
Derived1 ob1;
Derived2 ob2;
p = &baseob;
cout<<"p is pointing to an object of type ";
cout<<typeid(*p).name() <<endl;
p = &ob1;
cout<<"p is pointing to an object of type ";
cout<<typeid(*p).name() <<endl;
p = &ob2;
cout<<"p is pointing to an object of type ";
cout<<typeid(*p).name() <<endl;
return 0;
}
上面程序的输出如下:
p is pointing to an object of type class Base
p is pointing to an object of type class Derived1
p is pointing to an object of type class Derived2
正如程序输出的那样:当传入typeid()函数中的参数是一个具有多态性的基类指针的时候,在程序运行时指针指向的对象的实际类型会被返回。
当typeid()函数作用于不具有多态性的基类指针的时候,它总是返回该基类的类型。也就是说不管该指针指向对象类型是基类对象还是派生类的对象。我们可以把上面程序中的虚函数注释掉,观察程序的运行结果。我们可以看到函数返回的类型总是基类的类型。
由于typeid()通常总是和取指针指向对象的运算符*一起使用,就出现了针对空指针取其指向对象的一个特殊的异常:bad_typeid。也就是说此时typeid()会抛出该异常。
对于具有多态性的类层次的引用和也指针一样。此时,当传入typeid()的参数是对具有多态性的类的引用的时候,函数会返回对运行时实际对象的类型。此时就很有可能是一个派生类型。当我们通过引用来传递参数的时候就有可能使用到该特性。
typeid()函数的第二种形式:
typeid(类型名称)
例如:下面的形式是完全正确的:
cout<<typeid(int).name();
这种写法主要是用于通过类型来获取描述该类型的相关信息,以便可以用于进行类型比较。
练习:
1. 静态成员有什么特殊性?
2. typeid()是用来做什么的?
3. typeid()返回的是什么类型?