`
sealbird
  • 浏览: 570641 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

如何在运行时确定对象类型(RTTI)

    博客分类:
  • C++
阅读更多
引用
作者:NorthTibet



    RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息。它提供了运行时确定对象类型的方法。本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI;本文还将详细描述两个重要的 RTTI 运算符的使用方法,它们是 typeid 和 dynamic_cast。
    其实,RTTI 在C++中并不是什么新的东西,它早在十多年以前就已经出现了。但是大多数开发人员,包括许多高层次的C++程序员对它并不怎么熟悉,更不用说使用 RTTI 来设计和编写应用程序了。
    一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用 RTTI 机制。但是,在很多情况下,虚拟函数无法克服本身的局限。每每涉及到处理异类容器和根基类层次(如 MFC)时,不可避免要对对象类型进行动态判断,也就是动态类型的侦测。如何确定对象的动态类型呢?答案是使用内建的 RTTI 中的运算符:typeid 和 dynamic_cast。
    首先让我们来设计一个类层次,假设我们创建了某个处理文件的抽象基类。它声明下列纯虚拟函数:open()、close()、read()和 write():

class File
{
public:
virtual int open(const string & filename)=0;
virtual int close(const string & filename)=0;
//
virtual ~File()=0; // 记住添加纯虚拟析构函数(dtor)
};
现在从 File 类派生的类要实现基类的纯虚拟函数,同时还要提供一些其他的操作。假设派生类为 DiskFile,除了实现基类的纯虚拟函数外,还要实现自己的flush()和defragment()操作: class DiskFile: public File
{
public:
int open(const string & filename);

// 实现其他的纯虚拟函数
    ......

// 自己的专有操作
virtual int flush();
virtual int defragment();
};   
接着,又从 DiskFile 类派生两个类,假设为 TextFile 和 MediaFile。前者针对文本文件,后者针对音频和视频文件: class TextFile: public DiskFile
{
  // ......
  int  sort_by_words();
};

class MediaFile: public DiskFile
{
  //......
};       
我们之所以要创建这样的类层次,是因为这样做以后可以创建多态对象,如:File *pfile; // *pfile的静态类型是 File
if(some_condition)
  pfile = new TextFile; // 动态类型是 TextFile
else
  pfile = new DiskFile; // 动态类型是 DiskFile      
    假设你正在开发一个基于图形用户界面(GUI)的文件管理器,每个文件都可以以图标方式显示。当鼠标移到图标上并单击右键时,文件管理器打开一个菜单,每个文件除了共同的菜单项,不同的文件类型还有不同的菜单项。如:共同的菜单项有“打开”“拷贝”、和“粘贴”,此外,还有一些针对特殊文件的专门操作。比如,文本文件会有“编辑”操作,而多媒体文件则会有“播放”菜单。为了使用 RTTI 来动态定制菜单,文件管理器必须侦测每个文件的动态类型。利用 运算符 typeid 可以获取与某个对象关联的运行时类型信息。typeid 有一个参数,传递对象或类型名。因此,为了确定 x 的动态类型是不是Y,可以用表达式:typeid(x) == typeid(Y)实现:#include <typeinfo> // typeid 需要的头文件
void menu::build(const File * pfile)
{
if (typeid(*pfile)==typeid(TextFile))
{
  add_option("edit");
}
else if (typeid(*pfile)==typeid(MediaFile))
{
add_option("play");
}
}     
    使用 typeid 要注意一个问题,那就是某些编译器(如 Visual C++)默认状态是禁用 RTTI 的,目的是消除性能上的开销。如果你的程序确实使用了 RTTI,一定要记住在编译前启用 RTTI。使用 typeid 可能产生一些将来的维护问题。假设你决定扩展上述的类层次,从MediaFile 派生另一个叫 LocalizeMedia 的类,用这个类表示带有不同语言说明文字的媒体文件。但 LocalizeMedia 本质上还是个 MediaFile 类型的文件。因此,当用户在该类文件图标上单击右键时,文件管理器必须提供一个“播放”菜单。可惜 build()成员函数会调用失败,原因是你没有检查这种特定的文件类型。为了解决这个问题,你必须象下面这样对 build() 打补丁: void menu::build(const File * pfile)
{

//......

  else if (typeid(*pfile)==typeid(LocalizedMedia))
{
  add_option("play");
  }
}    
    唉,这种做法真是显得太业余了,以后每次添加新的类,毫无疑问都必须打类似的补丁。显然,这不是一个理想的解决方案。这个时候我们就要用到 dynamic_cast,这个运算符用于多态编程中保证在运行时发生正确的转换(即编译器无法验证是否发生正确的转换)。用它来确定某个对象是 MediaFile 对象还是它的派生类对象。dynamic_cast 常用于从多态编程基类指针向派生类指针的向下类型转换。它有两个参数:一个是类型名;另一个是多态对象的指针或引用。其功能是在运行时将对象强制转换为目标类型并返回布尔型结果。也就是说,如果该函数成功地并且是动态的将 *pfile 强制转换为 MediaFile,那么 pfile的动态类型是 MediaFile 或者是它的派生类。否则,pfile 则为其它的类型:void menu::build(const File * pfile)
{
if (dynamic_cast <MediaFile *> (pfile))
{
  // pfile 是 MediaFile 或者是MediaFile的派生类 LocalizedMedia
  add_option("play");
}
else if (dynamic_cast <TextFile*> (pfile))
{
  // pfile 是 TextFile 是TextFile的派生类
  add_option("edit");
}
}   
    细细想一下,虽然使用 dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响。





最新评论 [发表评论] [文章投稿]  查看所有评论  推荐给好友  打印

好文章,很清楚 ( maxint 发表于 2009-11-15 14:08:00)

不错,又学了点知识~~ ( naqinga 发表于 2008-10-15 9:37:00)

发扬一下吧。 ( boyfriend_1980 发表于 2008-8-8 13:17:00)

真的很好的文章,我喜欢的非常啊。 ( boyfriend_1980 发表于 2008-8-8 13:17:00)

写得很好啊,我在看《深入浅出MFC》的时候,对RTTI看的是一头雾水,现在心里总算有底了,要知道,建立对知识的框架结构是最难的,现在剩下的就是填充更详细的细节啦,谢谢作者!! ( yangmeng 发表于 2005-4-18 21:14:00)

分享到:
评论

相关推荐

    在运行时确定对象类型(RTTI)

    一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用 RTTI 机制。但是,在很多情况下,虚拟函数无法克服本身的局限。

    RTTI使用探讨与编码实现

    在C++中如果使用多态继承类,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况,这事就需要通过RTTI动态识别对象的类型。因此,本文探讨了RTTI的使用细节。同时,由于有些平台C++编译器(如...

    C++中的RTTI机制详解

    RTTI是”Runtime Type Information”的缩写,意思是运行时类型信息,它提供了运行时确定对象类型的方法。RTTI并不是什么新的东西,很早就有了这个技术,但是,在实际应用中使用的比较少而已。而我这里就是对RTTI进行...

    C++ 中RTTI的使用方法详解

    RTTI旨在为程序在运行阶段确定对象类型提供一种标准方式。很多类库已经成为其父类对象提供了实现这种方式的功能。但由于c++内部并不支持,因此各个厂商的机制通常互不兼容。创建一种RTTI语言标准将使得未来的库能够...

    深入理解Java类型信息(Class对象)与反射机制

    本篇主要是深入对Java中的Class对象进行分析,这对后续深入理解反射技术非常重要,主要内容如下:认识Class对象之前,先来了解一个概念,RTTI(Run-TimeTypeIdentification)运行时类型识别,对于这个词一直是C++中...

    C++入门到精通

    动态绑定是指 在运行时刻根据多态对象的实际类型来确定应该执行哪个操作 的解析能力 在C++中 这是通过虚拟函数机制来处理的 第 17 章介绍了面向对象程序设计的基本特性 这一章说明了如何设计和实现一个Query 类...

    tinyVm:用于学习目的的 tinyVM

    Eve 的特点是: 跨平台编译器面向对象语言命令式(过程式)编程函数重载运算符重载定义新运算符(不确定) 多线程(不确定) 静态类型(安全类型) 内存管理器运行时类型信息 RTTI 允许集成 C 库/源代码可以编译和...

    C++大学教程,一本适合初学者的入门教材(part2)

    21.7 运行时类型信息(RTTI) 21.8 运算符关键字 21.9 explicit构造函数 21.10 mutable类成员 21.11 类成员指针(.和—&gt;) 21.12 多重继承与virtual基类 21.13 结束语 小结 术语 自测练习 自测练习答案 练习 ...

    Google C++ 编码规范

    4) dynamic_cast:除测试外不要使用,除单元测试外,如果你需要在运行时确定类型信息,说明设计有缺陷(参考RTTI)。 10. 前置自增和自减(Preincrement and Predecrement) 对于迭代器和其他模板对象使用前缀...

    C++ Primer第四版【中文高清扫描版】.pdf

    15.5.1 名字查找在编译时发生 498 15.5.2 名字冲突与继承 498 15.5.3 作用域与成员函数 499 15.5.4 虚函数与作用域 500 15.6 纯虚函数 502 15.7 容器与继承 503 15.8 句柄类与继承 504 15.8.1 指针型句柄 505 15.8.2...

    Java开发技术大全 电子版

    第9章运行时类型识别288 9.1RTTI的作用288 9.2用Class类来加载对象289 9.3使用getClass()方法获取类信息290 9.4使用类标记292 9.5使用关键字instanceof判断所属类292 9.6利用反射获取运行时类信息293 9.6.1...

    net学习笔记及其他代码应用

    答:服务器端向客户端发送一个进程编号,一个程序域编号,以确定对象的位置。 24.在C#中,string str = null 与 string str = “” 请尽量使用文字或图象说明其中的区别。 答:string str = null 是不给他分配...

    C++大学教程,一本适合初学者的入门教材(part1)

    21.7 运行时类型信息(RTTI) 21.8 运算符关键字 21.9 explicit构造函数 21.10 mutable类成员 21.11 类成员指针(.和—&gt;) 21.12 多重继承与virtual基类 21.13 结束语 小结 术语 自测练习 自测练习答案 练习 ...

    Delphi5开发人员指南

    2.20 运行期类型信息 72 2.21 总结 72 第3章 Win32 API 73 3.1 对象:以前和现在 73 3.1.1 内核对象 73 3.1.2 GDI和用户对象 75 3.2 多任务和多线程 75 3.3 Win32内存管理 76 3.3.1 什么是线性内存模式 76 3.3.2 Win...

Global site tag (gtag.js) - Google Analytics