RTTI

编辑

RTTI(Run-Time Type Information),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
中文名
RTTI
外文名
Run-Time Type Information
属    于
程序
函    数
typeid

目录

  1. 1 RTTI介绍
  2. ▪ typeid函数
  3. ▪ type_info类
  4. ▪ typeid函数怎样创建type_info类的对象
  1. ▪ typeid函数的使用原理
  2. ▪ typeid函数使用方式
  3. ▪ 强制类型转换运算符
  4. ▪ dynamic_cast强制转换运算符
  1. ▪ dynamic_cast的注意事项
  2. 2 typeid的注意事项

RTTI介绍编辑

RTTI提供了以下两个非常有用的操作符:
(1)typeid操作符,返回指针和引用所指的实际类型。
(2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
面向对象的编程语言,象C++,Java,delphi都提供了对RTTI的支持。 本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI;本文还将详细描述两个重要的 RTTI 运算符的使用方法,它们是 typeid 和dynamic_cast。
其实,RTTI 在C++中并不是什么新的东西,它早在十多年以前就已经出现了。但是大多数开发人员,包括许多高层次的C++程序员对它并不怎么熟悉,更不用说使用 RTTI 来设计和编写应用程序了。
一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用 RTTI 机制。但是,在很多情况下,虚拟函数无法克服本身的局限。每每涉及到处理异类容器和根基类层次(如 MFC)时,不可避免要对对象类型进行动态判断,也就是动态类型的侦测。如何确定对象的动态类型呢?答案是使用内建的 RTTI 中的运算符:typeid 和 dynamic_cast。
在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。当类中含有虚函数时,其基类的指针就可以指向任何派生类的对象,这时就有可能不知道基类指针到底指向的是哪个对象的情况,类型的确定要在运行时利用运行时类型标识做出。为了获得一个对象的类型可以使用typeid函数,该函数反回一个对type_info类对象的引用,要使用typeid必须使用头文件<typeinfo>,因为typeid是一个返回类型为typ_info的引用的函数所以这里有必要先介绍一下type_info类

typeid函数

该函数的主要作用就是让用户知道当前的变量是什么类型的,比如使用typeid(a).name()就能知道变量a是什么类型的。因为typeid()函数是一个返回类型为const typeid_info&类型的函数,所以下面先对type_info类作下介绍

type_info类

该类的具体实现方式依编译器而定,但一般都有如下的成员定义
1
2
3
4
5
6
7
8
9
10
11
12
classtype_info
{
private:
type_info(consttype_info&);
type_info&operator=(consttype_info&);//type_info类的复制构造函数和赋值运算符是私有的。
public:
virtual~type_info();//析构函数
booloperator==(consttype_info&)const;//在type_info类中重载了==运算符,该运算符可以比较两个对象的类型是否相等。
booloperator!=(consttype_info&)const;//重载的!=运算符,以比较两个对象的类型是否不相等
constchar*name()const;//使用得较多的成员函数name,该函数反回对象的类型的名字。前面使用的typeid(a).name()就调用了该成员函数
boolbefore(consttype_info&);
};

因为type_info类的复制构造函数和赋值运算符都是私有的,所以不允许用户自已创建type_info的对象,比如type_info A;错误,没有默认的构造函数。唯一要使用type_info类的方法就是使用typeid函数。

typeid函数怎样创建type_info类的对象

该函数返回type_info类对象的引用,即形式为const type_info& typeid();因此也可以说typeid函数是type_info类的一个引用对象,可以访问type_info类的成员。但因为不能创建type_info类的对象,而typeid又必须返回一个类型为type_info类型的对象的引用,所以怎样在typeid函数中创建一个type_info类的对象以便让函数返回type_info类对象的引用就成了问题。这可能是把typid函数声明为了type_info类的友元函数来实现的,默认构造函数是私有的并不能阻止该类的友元函数创建该类的对象。所以typeid函数如果是友元的话就可以访问type_info类的私有成员,从而可以创建type_info类的对象,从而可以创建反回类型为type_info类的引用。
举个例子:
class A{private:A(){} A(const A&){} A& operator =(const A&){} friend A& f();};这里把类A的默认构造函数,复制构造函数和赋值操作符定为私有从而防止创建类A的对象,但函数f()是类A的友元,所以在函数f()中可以创建类A的对象。同时为了实现函数f()返回的对象类型是A的引用,就必须在函数f中创建一个类A的对象以作为函数f的返回值,比如函数f可以这样定义A& f(){A ma; cout<<”f”<<endl; return ma}。
因为typeid函数是type_info类的对象,也就是说可以用该函数访问type_info类的成员,即type_info类中重载的= =和!=运算符,name()和before()成员函数,比如typid(a).name()和typid(a) == typid(b)等等。

typeid函数的使用原理

该函数的形式为type_info& typeid(object)其中object是任何类型的对象,可以是内置类型和用户创建的类类型。可以看出typeid即是一个函数,同时他也是type_info类的对象,即typeid可以访问类type_info类的成员,也可以做为一个单独的函数来使用。做个简单的例子,比如
class A{private: A(){b=3;cout<<”A”<<endl;} //私有的默认构造函数
public: void name(){cout<<”NA”<<endl;} int b;
friend A f();}; //函数f()是类A的友元,因此在f中可以创建类A的对象。
A f() //函数f()在这里即是类A的一个对象,也是一个单独的函数。
{ A m; //创建类A的对象,因为函数f是类A的友元,因此可以创建类A的对象
cout<<”F”<<endl; return m;}
main()
{ f().name(); //函数f()作为类A的对象使用,这里要注意程序的执行顺序,首先执行函数f()中的语句A m,因此调用类A的默认构造函数输出A,然后执行A m;后面的语句,输出F,再然后调用类A中的成员函数name输出NA.
f(); } //函数f()单独作为函数使用。
我们创建一个类A,其中A的默认构造函数是私有的,也就是说不能用默认构造函数创建类A的对象。函数f()是类A的友元,且返回一个类A的对象,因为f()函数是类A的友元,所以在函数f中可以用默认构造函数创建类A的对象,这时函数f()同时是一个函数,也是类A的对象,因此也可以访问类A中的成员。

typeid函数使用方式

  1. 、使用type_info类中的name()成员函数反回对象的类型的名称。其方法为:typeid(object).name()其中object是要显示其相应类型名的对象,该函数反回的名字因编译器而定。这里要注意的就是使用方式一中提到的虚函数类型的问题,即如果有类A,且有虚函数,类B,C,D都是从类A派生的,且都重定义了类A中的虚函数,这时有类A的指针p,再把对象类B的对象的地址赋给指针p,则typeid(p).name()将反回的类型将是A*,因为这里的p表示的是一个指针,该指针是类型为A的指针,所以返回A*,而typeid(*p).name()将返回B,因为指针p是指向类B的对象的,而*p就表示的是类B的对象,所以返回B。
2)、使用type_info类中重载的= =与!=比较两个对象的类型是否相等。使用该方法需要调用类type_info中重载的= =和!=操作符,其使用方法为typid(object1)= =typid(object2);如果两个对象的类型相等则返回1,如果不相等则为0。这种使用方法通常用于比较两个带有虚函数的类的对象是否相等,比如有类A,其中定义有虚函数,而类B,类C,类D,都是从类A派生而来的且重定义了该虚函数,这时有两个类A的指针p和p1,按照虚函数的原理,基类的指针可以指向任何派生类的对象,在这时就有可能需要比较两个指针是否指向同一个对象,这时就可以这样使用typeid了,typeid(*p)= =typeid(*p1);这里要注意的是typeid(*p)与typeid(p)是指的不同的对象类型,typeid(p)表示的是p的类型,在这里p是一个指针,这个指针指向的是类A的对象,所以p的类型是A*,而typeid(*p)则不一样,*p表示的是指针p实际所指的对象的类型,比如这里的指针p指向派生类B,则typeid(*p)的类型为B。所以在测试两个指针的类型是否是相等时应使用*p,即typeid(*p)= =typeid(*p1)。如果是typeid(p)= =typeid(p1)的话,则无论指针p和p1指向的什么派生类对象,他们都是相等的,因为都是A *的类型。

强制类型转换运算符

C++有四种强制类型转换符,分别是dynamic_cast,const_cast,static_cast,reinterpret_cast。其中dynamic_cast与运行时类型转换密切相关,在这里我们介绍dynamic_cast。

dynamic_cast强制转换运算符

该转换符用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用,注意dynamic_cast转换符只能用于含有虚函数的类,其表达式为dynamic_cast<类型>(表达式),其中的类型是指要将表达式转换成的目标类型,比如含有虚函数的基类B和从基类B派生出的派生类D,则B *pb; D *pd, md; pb=&md; pd=dynamic<D*>(pb); 最后一条语句表示把指向派生类D的基类指针pb转换为派生类D的指针,然后将这个指针赋给派生类D的指针pd,有人可能会觉得这样做没有意义,既然指针pd要指向派生类为什么不pd=&md;这样做更直接呢?有些时候我们需要强制转换,比如如果指向派生类的基类指针B想访问派生类D中的除虚函数之外的成员时就需要把该指针转换为指向派生类D的指针,以达到访问派生类D中特有的成员的目的,比如派生类D中含有特有的成员函数g(),这时可以这样来访问该成员dynamic_cast<D*>(pb)->g();因为dynamic_cast转换后的结果是一个指向派生类的指针,所以可以这样访问派生类中特有的成员。但是该语句不影响原来的指针的类型,即基类指针pb仍然是指向基类B的。如果单独使用该指针仍然不能访问派生类中特有的成员。一般情况下不推荐这样使用dynamic_cast转换符,因为dynamic_cast的转换并不会总是成功的,具体情况在后面介绍。

dynamic_cast的注意事项

dynamic_cast转换符只能用于指针或者引用。dynamic_cast转换符只能用于含有虚函数的类。dynamic_cast转换操作符在执行类型转换时首先将检查能否成功转换,如果能成功转换则转换之,如果转换失败,如果是指针则反回一个0值,如果是转换的是引用,则抛出一个bad_cast异常,所以在使用dynamic_cast转换之间应使用if语句对其转换成功与否进行测试,比如pd=dynamic_cast<D*>(pb); if(pd){…}else{…},或者这样测试if(dynamic_cast<D*>(pb)){…}else{…}。

typeid的注意事项编辑

使用 typeid 要注意一个问题,那就是某些编译器(如 Visual C++)默认状态是禁用 RTTI 的,目的是消除性能上的开销。如果你的程序确实使用了 RTTI,一定要记住在编译前启用 RTTI。(vc6.0启用方式:project->setting->c/c++->category->c++ Language 下面第二个复选框选中)。使用 typeid 可能产生一些将来的维护问题。假设你决定扩展上述的类层次,从MediaFile 派生另一个叫 LocalizeMedia 的类,用这个类表示带有不同语言说明文字的媒体文件。但 LocalizeMedia 本质上还是个 MediaFile 类型的文件。因此,当用户在该类文件图标上单击右键时,文件管理器必须提供一个“播放”菜单。可惜 build()成员函数会调用失败,原因是你没有检查这种特定的文件类型。为了解决这个问题,你必须象下面这样对 build() 打补丁:
1
2
3
4
5
6
7
8
voidmenu::build(constFile*pfile)
{
//......
elseif(typeid(*pfile)==typeid(LocalizedMedia))
{
add_option("play");
}
}

唉,这种做法真是显得太业余了,以后每次添加新的类,毫无疑问都必须打类似的补丁。显然,这不是一个理想的解决方案。这个时候我们就要用到 dynamic_cast,这个运算符用于多态编程中保证在运行时发生正确的转换(即编译器无法验证是否发生正确的转换)。用它来确定某个对象是 MediaFile 对象还是它的派生类对象。dynamic_cast 常用于从多态编程基类指针向派生类指针的向下类型转换。它有两个参数:一个是类型名;另一个是多态对象的指针或引用。其功能是在运行时将对象强制转换为目标类型并返回布尔型结果。也就是说,如果该函数成功地并且是动态的将 *pfile 强制转换为 MediaFile,那么 pfile的动态类型是 MediaFile 或者是它的派生类。否则,pfile 则为其它的类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
voidmenu::build(constFile*pfile)
{
if(dynamic_cast<MediaFile*>(pfile))
{
//pfile是MediaFile或者是MediaFile的派生类LocalizedMedia
add_option("play");
}
elseif(dynamic_cast<TextFile*>(pfile))
{
//pfile是TextFile是TextFile的派生类
add_option("edit");
}
}

细细想一下,虽然使用 dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响。

RTTI-运行时类型识别相关推荐

  1. java 运行时类型_Java基础之RTTI 运行时类型识别

    运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于R ...

  2. 深入浅出MFC学习笔记:MFC六大关键技术仿真之RTTI运行时类型识别

    RTTI(运行时类型识别) 参考文献:深入浅出MFC-侯捷 怎样去构造类别型录网? 一.定义数据结构: 其中pFirstClass指针属于痊愈变量,所以它应该以static修饰之. 而且我们最终希望达 ...

  3. C++ 学习笔记之(19) new、delete表达式、RTTI(运行时类型识别)、枚举、类成员指针、嵌套类、局部类、位域、volatile、extern C

    C++ 学习笔记之(19) new.delete表达式.RTTI(运行时类型识别).枚举.类成员指针.嵌套类.局部类.位域.volatile.extern C C++ 学习笔记之(19) new.de ...

  4. Java RTTI运行时类型识别

    RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型. RTTI提供了以下两个非常有用的操作 ...

  5. java rtti_举例讲解Java的RTTI运行时类型识别机制

    1.RTTI:运行时类型信息可以让你在程序运行时发现和使用类型信息. 在Java中运行时识别对象和类的信息有两种方式:传统的RTTI,以及反射.下面就来说下RTTI. RTTI:在运行时,识别一个对象 ...

  6. 白话C++系列(27) -- RTTI:运行时类型识别

    http://www.cnblogs.com/kkdd-2013/p/5601783.html RTTI-运行时类型识别 RTTI:Run-Time Type Identification. 那么RT ...

  7. MFC六大核心机制之二:运行时类型识别(RTTI)

    上一节讲的是MFC六大核心机制之一:MFC程序的初始化,本节继续讲解MFC六大核心机制之二:运行时类型识别(RTTI). typeid运算子 运行时类型识别(RTTI)即是程序执行过程中知道某个对象属 ...

  8. c++远征之多态篇——运行时类型识别(RTTI)

    以下内容源于慕课网的学习整理,如有侵权,请告知删除. 1.RTTI(Run-Time Type Information),运行时类型识别. 涉及typeid.dynamic_cast这两个知识点. R ...

  9. RTTI机制(运行时类型识别)

    RTTI机制(运行时类型识别) 在多态里面,基类里的虚函数和派生类里的虚函数形成了遮蔽,这就导致在主程序运行时,有些表达式的类型没有办法确定.必须等到程序运行结束后,根据具体的环境才能确定.看下面的代 ...

  10. C++11 的 运行时类型识别type_info

    一.type_info与typeid 类type_info保存关于类型的特定于实现的信息,包括类型的名称,以及比较两个类型是否相等或排序顺序的方法. 这是typeid操作符返回的类.具有如下特点: ( ...

最新文章

  1. 不要争了,别的程序员是这么给变量起名的!
  2. 转载:中年程序猿的迷茫,你还在深究技术吗?
  3. localdate计算相差天数_还在苦恼MySQL如何根据日期精确计算年龄?看这一篇,就够了
  4. 常见电脑字符编码总结
  5. SUSE Linux启动过程执行脚本顺序
  6. 人脸识别腾讯安排上了!孩子不能再任意冒用家长身份信息
  7. unrecognized selector sent to instance
  8. R语言包下载(转载)
  9. 【梳理】离散数学 第19章 初等数论 19.3 同余 19.4 一次同余方程
  10. “华硕zx50j,键盘背光灯无法显示,fn组合键不可用”解决办法
  11. Python进阶之八皇后算法
  12. Java我的世界forge安装失败,我的世界forge安装失败install怎么办
  13. 家有千金之冰糖绿豆汤
  14. 实战分析APP提交苹果App Store审核被拒绝的一些原因以及对策
  15. RxJS 6 —— operators
  16. Windows找回磁盘被病毒感染后隐藏的文件
  17. 17-11-01模拟赛
  18. 读《人性的优点》有感
  19. 大疆FPGA/芯片开发工程师(B卷)笔试题(含详解)
  20. 怎么查快递到哪里了?快递单号查询全部物流

热门文章

  1. idea代码可以编译但是爆红_推荐一款 IDEA 生成代码神器,写代码再也不用加班了...
  2. jvm垃圾回收机制_干货|JVM垃圾回收机制
  3. Java构造函数可以私有,我们可以在Java中使用私有的构造函数吗?
  4. java 获取所有带指定注解的类名_SXT DAY023 反射和注解
  5. truncate python是删除文件内容吗_在Python中操作文件之truncate()方法的使用教程
  6. python数据分析兼职能挣钱吗_Python开发能从事数据分析吗
  7. 作为Java程序员,这些开源工具你应该要学习!
  8. 伪类如何动态在html设置样式,用js实现before和after伪类的样式修改的示例代码
  9. python字典替换值_python字典改变value值方法总结
  10. 邢台职业技术学院计算机系宿舍,邢台职业技术学院宿舍条件怎么样 男生女生宿舍图片...