运行时类型信息RTTI(run time type identification)就两个:typeid和dynamic_cast。其类型信息来自于运行时,所以这两个主要应用于实现多态的父子类之间。
一、typeid
typeid是一种运算符,可以获得变量包括对象的类型,作为一个获取运行时类型信息的运算符,目的并不是获取普通变量的类型,而是对象指针的类型。
#include<iostream>
#include<typeinfo>
using namespace std;
class A {
public:
virtual ~A() {}
};
class B :public A {
public:
B() {}
};
int main() {
B b;
A *pa = &b;
cout << typeid(pa).name() << endl;
cout << typeid(*pa).name() << endl;
system("pause");
return 0;
}
输出结果:
typeid(pa)会生成一个对象,然后调用name函数,可以看到typeid(pa).name()获得的仍然还是A的类型,只有typeid(*pa).name()才可以或者其真正的类型是B。
typeid重载了操作符== !=进行比较,函数name()则返回类型名称,typeid的赋值操作符重载和拷贝构造均是私有的,因此外部不能对其进行赋值或者拷贝的操作。
typeid主要根据返回类型,用作调试。
if(typeid(*pa).name() != typeid(b).name())
cout << typeid(pa).name() << endl;
if(typeid(*pa).name() == typeid(b).name())
cout << typeid(*pa).name() << endl;
像这样先判断pa实际指向的是什么类型的对象,然后再进行操作。
二、dynamic_cast
具有继承关系的父子类之间,子类是可以隐式转换成父类的,但父类不能隐私转换成子类,那么必然可以显示转换了,这里就用到了dynamic_cast。
首先说明一点,dynamic_cast只能用于具有虚函数的父类转换成子类,否则就会报错。
class A {
public:
virtual ~A() {}
};
class B :public A {
public:
B() {}
};
class C : public A {
};
class D {};
int main() {
B b;
A *pa = &b;
B *pb = dynamic_cast<B*>(pa);
cout << pb << endl;
C *pc = dynamic_cast<C*>(pa);
cout << pc << endl;
D *pd = dynamic_cast<D*>(pa);
cout << pd << endl;
system("pause");
return 0;
}
输出结果:
dynamic_cast先检查指针所指的类型是否含有虚函数,含有,则编译通过,否则编译就会出错。再者判断该指针所指向的类型与要转换的类型是否具有一种“is a”的关系,也就是说父子类的关系,如果是“is a”的关系,dynamic_cast就会返回对象的地址,否则,dynamic_cast会返回NULL。
这也是为什么上面例子中后两个地址都是0的关系。对于:
C *pc = dynamic_cast<C*>(pa);
虽然C继承自A,但pa这个指针实际指向的是B类对象,也就是说将B类对象转换成C类,现在不是“is a”的关系,故dynamic_cast会返回一个NULL。
从dynamic_cast的返回值可以看出dynamic_cast是很安全的,可以转换的转,不可以的成功,但会返回NULL。
int main() {
B b;
B *pbb;
A *pa = &b;
B *pb = dynamic_cast<B*>(pa); //成功 安全
cout << pb << endl;
C *pc = dynamic_cast<C*>(pa); //成功 安全
cout << pc << endl;
D *pd = dynamic_cast<D*>(pa); //成功 安全
cout << pd << endl;
cout << "--------------------------" << endl;
pb = static_cast<B*>(pa); //成功 不安全
cout << pb << endl;
pc = static_cast<C*>(pa); //成功 不安全
cout << pc << endl;
/*pd = static_cast<D*>(pa); //编译不通过
cout << pd << endl;*/
cout << "--------------------------" << endl;
pb = reinterpret_cast<B*>(pa); //成功 不安全
cout << pb << endl;
pc = reinterpret_cast<C*>(pa); //成功 不安全
cout << pc << endl;
pd = reinterpret_cast<D*>(pa); //成功 不安全
cout << pd << endl;
cout << "--------------------------" << endl;
system("pause");
return 0;
}
这里让dynamic_cast与static_cast和reinterpret_cast这两种类型转换进行比较。
输出结果:
显然dynamic_cast要安全多了。reinterpret_cast是相当不安全的:
class A {
public:
virtual ~A() {}
};
class B : public A {
public:
B() {}
};
class C : public A {
public:
C() {}
void fun() {
cout << "class C" << endl;
}
};
class D {
public:
void fun() {
cout << "class D" << endl;
}
};
int main() {
B b;
A *pa = &b;
C *pc = reinterpret_cast<C*>(pa);
cout << pc << endl;
pc->fun();
D *pd = reinterpret_cast<D*>(pa);
cout << pd << endl;
pd->fun();
system("pause");
return 0;
}
我在C类和D类中放入了自己独有的成员,同样强制转换,输出结果:
可以看到pa实际指向的B类对象分别顺利的转成了毫无关系的C和D,并且非常顺利的调用了专属于这两个类的方法,这是相当可怕的。