在C++ 环境中﹐头文件(header file) 含有类之定义(class definition)亦即包含有关类的结构资料(representational information)。但是﹐这些资料只供编译器(compiler)使用﹐编译完毕后并未留下来﹐所以在执行时期(at run-time) ﹐无法得知对象的类资料﹐包括类名称、数据成员名称与类型、函数名称与类型等等。
例如﹐两个类﹐Circle继承Shape类,若有如下指令﹕
Shape*p;
p = new Circle();
Shape&q = *p;
在执行时﹐p 指向一个对象﹐但欲得知此对象之类资料(这里没有实现虚函数)﹐就有困难了。同样欲得知q 所参考(reference)对象的类资料﹐也无法得到。
RTTI(Run-Time Type Identification)的存在就是要解决这困难﹐也就是在执行时﹐您想知道p指针所指到或参考到的对象类型﹐到底是Circle呢还是Shap类呢?该对象有能力来告诉您。
RTTI提供了两个十分有用的操作符:(1) typied操作符,返回指针和应用的实际类型(比如使用typeid(a).name()就能知道变量a是什么类型的)。
(2) dynamic_cast操作符 ,将基类类型的指针或引用安全地转换为派生类型的指针或引用。在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。当类中含有虚函数的时候,其基类的指针可以指向任何派生类的对象,这时就有可能不知道基类到底指向的是那个对象的情况,类型的确定要在运行时利用运行时类型标识做出。为了获得对象的类型可以使用typeid函数。
typeid函数:主要作用是让用户知道当前的变量是什么类型的,并且对于自己定义的结构体,类都能很好的支持。用法如下:
- class A{};
- A *a=new A;
- cout<<typeid(a).name()<<endl;
- cout<<typeid(*a).name()<<endl;
class A{};
A *a=new A;
cout<<typeid(a).name()<<endl;
cout<<typeid(*a).name()<<endl;
运行结果:
RTTI的实现:
例如 Circle和Square都集成Shape类﹐它们各有自己的draw()函数。当C++ 提供了RTTI﹐就可写个函数如下﹕
void drawing( Shape *p )
{
if( typeid(*p).name() == “Circle” )
((Circle*)p) -> draw();
if( typeid(*p).name() == “Rectangle” )
((Rectangle*)p) -> draw();
}
虽然drawing() 函数也具有多型性﹐但它与Figure类体系的结构具有紧密的相关性。当shape类体系再派生出子类时﹐drawing() 函数的内容必须多加个if指令。因而违反
了「开放╱封闭原则」﹐如下﹕很显然地﹐drawing() 函数应加以修正。
想一想﹐如果C++ 并未提供RTTI﹐则程序员毫无选择必须使用虚函数来支持drawing() 函数的多型性。于是乎写下:virtual void drawing()﹕
void drawing(Figure *p)
{
p->draw();
}
如此﹐Figure类体系能随时派生类﹐而不必修正drawing() 函数。亦即﹐Figure体系有个稳定的接口(interface) ﹐drawing() 使用这接口﹐使得drawing() 函数也稳定
﹐不会随Figure类体系的扩充而变动。这是封闭的一面。而这稳定的接口并未限制Figure体系的成长﹐这是开放的一面。因而合乎「开放╱封闭」原则﹐软件的结构会更具弹性﹐更易于随环境而不断成长。
二、一般而言﹐RTTI的常见使用场合有四﹕1.异常处理
大家所熟悉的C++ 新功能﹕异常处理﹐其需要RTTI﹐如类名称等。
2.动态转类型
在类体系(class hierarchy) 中﹐往下的类型转换需要类继承的RTTI。
3. 模块集成
当某个程序模块里的对象欲跟另一程序模块的对象沟通时﹐应如何得知对方的身分呢﹖
知道其身分资料﹐才能呼叫其函数。一般的C++ 程序﹐常见的解决方法是──在源代码中把对方对象之类定义(即存在头文件里)包含进来﹐在编译时进行连结工作。然而﹐像目前流行的主从(Client-Server) 架构中﹐客户端(client)的模块对象﹐常需与主机端(server)的现成模块对象沟通﹐它们必须在执行时沟通﹐但又常无法一再重新编译。于是靠标 头文件来提供的类定义资料﹐无助于执行时的沟通工作﹐只得依赖RTTI了。4.对象I/O
C++ 程序常将其对象存入数据库﹐未来可再读取之。对象常内含其它小对象﹐因之在存入数据库时﹐除了必须知道对象所属的类名称﹐也必须知道各内含小对象之所属类﹐才能完整地将对象存进去。储存时﹐也将这些RTTI资料连同对象内容一起存入数据库中。未来﹐读取对象时﹐可依据这些RTTI资料来分配内存空间给对象。
博文资料参考:
http://www.cnblogs.com/lzjsky/archive/2010/11/23/1885771.html
http://blog.csdn.net/pi9nc/article/details/21742355
感谢博主的分享!!