问题:假设有一个类层次结构,一个基类派生了很多类,则可以让基类的指针指向其中任何一个类的对象,那通过指针指向对象的时候,如何判断对象的类型呢?
C++拥有RTTI(runtime type identification)这个特性,这个特性是通过在虚函数表中增加一项,用来存储具备多态性质的类的类型描述器。
RTTI:运行阶段可以确定对象的类型。C++有3个支持RTTI的元素
- dynamic_cast操作符将使用一个指向基类的指针来生成一个指向派生类的指针,如不能转换,该操作符返回空指针(这是因为dynamic_cast可以在执行向下转换的时候进行检查,如果可以可以转换,就传回适当转换过的指针,如果不能向下转换,就返回0)
- typeid操作符返回一个引用或者指针指向的对象的类型的值
- type_info类存储了有关特定类型的信息
type_info类内部实现的运算符有“==”,“!=”,实现的函数有type_info::name(),type_info::before(),name()是传回类的原始名称,before()是为了实现type_info对象之间的某些排序算法。
PS:只能将RTTI用于包含虚函数的类层次结构,原因在于只有对于这种类层次结构,才能将派生类对象的地址赋给基类指针
RTTI用于引用的时候和指针情况不同,因为对于指针来说,可以对其赋0以表示其没有指向任何对象,但是引用如果被赋0,则会产生一个临时对象,该临时对象的初值为0,这个引用被设定为该临时对象的一个别名。因此dynamic_cast对引用操作的时候,不能提供和指针一样的true/false检测。通常是抛出一个异常用以确定类型转换失败。
RTTI只适用于包含虚函数的类(具有多态性质)
如果对内建类型和非多态类类型执行type_id的话:
int *ptr;
if(typeid(ptr) == typeid(int *)){ ... }
int a;
typeid(a);
typeid(double);
会传回一个const type_info &,这与多态类型的差异在于,这时候的type_info对象的静态取得,而不是执行期取得。
type_id操作符使得能够确定两个对象是否为同种类型。可以接收类名和结果为对象的表达式为参数
type_id返回一个对type_info对象的引用
type_info包含一个name()成员,该函数返回一个字符串,类容随实现而异,通常是类的名称。
实验代码如下:
#include <iostream>
using namespace std;
class A
{
public:
virtual void fun(){cout << "A" << endl;} //虚函数
};
class B:public A
{
void fun(){cout << "B" << endl;} //子类重写
};
int main()
{
A a, *pa;
B b, *pb;
//pa = &b;
pa = dynamic_cast<class A *>(&b); //用两行中的任何一个都可以
pb = &b; //继承类的指针不能指向基类
const type_info &obj = typeid(a);
cout << obj.name() << endl;
const type_info &obj2 = typeid(pa);
cout <<obj2.name() << endl;
const type_info &obj3 = typeid(*pa);
cout <<obj3.name() << endl;
return 0;
};
实验结果为
class A
class A *
class B
如果去掉基类的虚函数,那么返回的结果就为
class A
class A *
class A
通过这两个操作符可以在程序运行时知道传入函数的基类指针指向的对象究竟是基类还是派生类,在程序中可以进行不同的判断。这也是C++多态的一个体现。
(如果是通过虚函数的继承,那么基类的指针可以自动调用派生类重写的函数。)