通过运行时类型识别(RTTI), 我们能够使用基类的指针或引用来判定这些指针或引用实际指向对象的类型。这要求基类中至少要有一个虚函数。
RTTI实现的基石是每个类型对应的一个const type_info类型对象,它存储了这个对象的确切类型信息。注意,一个类型对应一个type_info对象,而不是一个对象。无论是基本类型还是用户自定义类型,都需要额外的内存来存放此类型对应的type_info对象。一般情况,一个类型对应一个type_info对象。有的时候,需要为一种类型产生多个type_info对象:一个类继承自多个继承分支,并且多于或等于2个继承分支上存在多态类。
type_info类重载了operator=()、operator!=(),另外一个常用的成员函数是name(),name成员函数返回字符串表示的类型信息。判定的方法包dynamic_cast和typeid:
1、 dynamic_cast 动态转化
typeid不具备可扩展性,因为它返回一个对象的确切类型信息,不能将派生类匹配其父类。一个派生类如果是public继承,那么在语义上也应该可以看做基类对象。显然typeid不具备这个能力。dynamic_cast同时具有运行时类型识别和转换匹配2个功能。语法是:dynamic_cast<dest>(src);dest和src都必须为指针或者引用。其运行结果可以这样描述:如果运行时src和dest所引用的对象,是相同类型,或者存在is-a关系(public继承),则转换成功。否则失败。
dynamic_cast可以用来转换指针和引用,不能转化对象。它只能用来转换多态类型的对象指针或引用。
如果目标类型是指针时,成功则返回目标类型的指针,失败返回NULL。如果目标类型是引用,成功则返回引用,失败抛出std::bad_cast异常,因为不存在NULL引用。
dynamic_cast的运行时类型识别功能的实现,和typeid一样,通过指针或引用所绑定对象的虚函数表指针vptr,从vtable的第一项的type_info指针获得对象类型对应的type_info对象。那么,dynamic_cast的第二个功能“运行时类型的转换匹配”如何实现的呢?为了实现这个功能,RTTI机制必须维护一棵继承树,dynamic_cast通过遍历这个继承树来确定一个待转换的对象和目标类型之间是否存在is-a关系。dynamic_cast的运行开销显然要比虚函数调用和typeid大,而且其开销会随着源对象类型与目标类型之间的层次的增大而增大。
使用dynamic_cast注意:转换引用时,要有try、catch语句。转换指针时,检查是否转换结果为NULL。
2、 typeid使用
typeid(e) e是表达式类型名,如果e是含有虚函数的类型的指针或引用,则会对应其动态类型,否则对应静态类型。typeid()运算符和sizeof运算符一样,是C++语言直接支持的。它以一个对象或者类型名作为参数,返回一个对应于该类型的const type_info对象(其实是这个类型对应的type_info对象的引用),表明该
对象的确切类型。也可以使用typeid来查看非多态型对象和基本数据类型对象的类型信息。只不过,此时它不会去检索对象的vptr和vtable,它们根本就没有这些设施。此时typeid通过编译器维护的信息来返回结果,其结果仍然是操作数静态类型对应的type_info对象。
在多态类的对象中,存在一个虚函数表指针vptr,指向类型对应的虚函数表vtable。vtable的第一项为一个type_info指针,指向该类型对应的type_info对象。vtable从第二项开始,就是该类型的虚函数指针了。
对多态类对象应用typeid操作符,需要检索vptr指针,从vtable的第一项获得该对象类型对应的type_info对象。这个过程和虚函数的动态绑定是相同的,它们的代价相同。
使用typeid的时候,需要注意:typeid()括号中的可以是引用,但使用指针的时候要解引用指针,如typeid(*p)。*p和p的type_info信息是完全不同的。另外,对空指针进行typeid调用,会抛出std::bad_typeid异常。
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public:
virtual ~A() {};
int ta;
};
class B: public A
{
int tb;
};
int _tmain(int argc, _TCHAR* argv[])
{
A* a;
a = new B;
if(B* b = dynamic_cast<B*>(a))
{
cout<<"a是B类型指针"<<endl;
}
else
{
cout<<"a不是B类型指针"<<endl;
}
A a1;
B b1,b2;
A& ra1 = b1;
A& ra2 = a1;
A& ra3 = b2;
try
{
B& b = dynamic_cast<B&>(b1);
cout<<"b1是B类型引用"<<endl;
}
catch(bad_cast)
{
cout<<"b1不是B类型引用,发生异常"<<endl;
}
try
{
B& b = dynamic_cast<B&>(a1);
cout<<"a1是B类型引用"<<endl;
}
catch(bad_cast)
{
cout<<"a1不是B类型引用,发生异常"<<endl;
}
int i;
cout<<(typeid(i)==typeid(int))<<endl; //输出为1,及i是整数类型,这是静态类型
cout<<(typeid(a1)==typeid(b1))<<endl; //输出0,静态类型比较
cout<<(typeid(ra1)==typeid(ra2))<<endl; //输出0
cout<<(typeid(ra1)==typeid(ra3))<<endl; //输出为1,动态类型,引用都指向了B
cout<<(typeid(ra1)==typeid(B))<<endl; //输出为1
typedef unsigned int UINT;
cout << typeid(UINT).name() << endl; //"unsigned int"
cout << typeid(int).name() << endl; //"int"
cout << typeid(string).name() << endl; //"string"
cout << typeid(a1).name() << endl;
cout << typeid(ra1).name() << endl;
cout << typeid(ra2).name() << endl;
}
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public:
virtual ~A() {};
int ta;
};
class B: public A
{
int tb;
};
int _tmain(int argc, _TCHAR* argv[])
{
A* a;
a = new B;
if(B* b = dynamic_cast<B*>(a))
{
cout<<"a是B类型指针"<<endl;
}
else
{
cout<<"a不是B类型指针"<<endl;
}
A a1;
B b1,b2;
A& ra1 = b1;
A& ra2 = a1;
A& ra3 = b2;
try
{
B& b = dynamic_cast<B&>(b1);
cout<<"b1是B类型引用"<<endl;
}
catch(bad_cast)
{
cout<<"b1不是B类型引用,发生异常"<<endl;
}
try
{
B& b = dynamic_cast<B&>(a1);
cout<<"a1是B类型引用"<<endl;
}
catch(bad_cast)
{
cout<<"a1不是B类型引用,发生异常"<<endl;
}
int i;
cout<<(typeid(i)==typeid(int))<<endl; //输出为1,及i是整数类型,这是静态类型
cout<<(typeid(a1)==typeid(b1))<<endl; //输出0,静态类型比较
cout<<(typeid(ra1)==typeid(ra2))<<endl; //输出0
cout<<(typeid(ra1)==typeid(ra3))<<endl; //输出为1,动态类型,引用都指向了B
cout<<(typeid(ra1)==typeid(B))<<endl; //输出为1
typedef unsigned int UINT;
cout << typeid(UINT).name() << endl; //"unsigned int"
cout << typeid(int).name() << endl; //"int"
cout << typeid(string).name() << endl; //"string"
cout << typeid(a1).name() << endl;
cout << typeid(ra1).name() << endl;
cout << typeid(ra2).name() << endl;
}