上一节讲的是MFC六大核心机制之一:MFC程序的初始化,本节继续讲解MFC六大核心机制之二:运行时类型识别(RTTI)。

typeid运算子

运行时类型识别(RTTI)即是程序执行过程中知道某个对象属于某个类,我们平时用C++编程接触的RTTI一般是编译器的RTTI,即是在新版本的VC++编译器里面选用“使能RTTI”,然后载入typeinfo.h文件,就可以使用一个叫typeid()的运算子,它的地位与在C++编程中的sizeof()运算子类似的地方(包含一个头文件,然后就有一个熟悉好用的函数)。typdid()关键的地方是可以接受两个类型的参数:一个是类名称,一个是对象指针。所以我们判别一个对象是否属于某个类就可以象下面那样:

C++代码
  1. if (typeid (ClassName)== typeid(*ObjectName)){
  2. ((ClassName*)ObjectName)->Fun();
  3. }

像上面所说的那样,一个typeid()运算子就可以轻松地识别一个对象是否属于某一个类,但MFC并不是用typeid()的运算子来进行动态类型识别,而是用一大堆令人费解的宏。很多学员在这里很疑惑,好象MFC在大部分地方都是故作神秘。使们大家编程时很迷惘,只知道在这里加入一组宏,又在那儿加入一个映射,而不知道我们为什么要加入这些东东。

其实,早期的MFC并没有typeid()运算子,所以只能沿用一个老办法。我们甚至可以想象一下,如果MFC早期就有template(模板)的概念,可能更容易解决RTTI问题。

所以,我们要回到“古老”的年代,想象一下,要完成RTTI要做些什么事情。就好像我们在一个新型(新型到我们还不认识)电器公司里面,我们要识别哪个是电饭锅,哪个是电磁炉等等,我们要查看登记的各电器一系列的信息,我们才可以比较、鉴别,那个东西是什么!

CRuntimeClass链表的设计

要登记一系列的消息并不是一件简单的事情,大家可能首先想到用数组登记对象。但如果用数组,我们要定义多大的数组才好呢,大了浪费空间,小了更加不行。所以我们要用另一种数据结构——链表。因为链表理论上可大可小,可以无限扩展。

链表是一种常用的数据结构,简单地说,它是在一个对象里面保存了指向下一个同类型对象的指针。我们大体可以这样设计我们的类:

C++代码
  1. struct CRuntimeClass
  2. {
  3. ……类的名称等一切信息……
  4. CRuntimeClass * m_pNextClass;//指向链表中下一CRuntimeClass对象的指针
  5. };

链表还应该有一个表头和一个表尾,这样我们在查链表中各对象元素的信息的时候才知道从哪里查起,到哪儿结束。我们还要注明本身是由哪能个类派生。所以我们的链表类要这样设计:

C++代码
  1. struct CRuntimeClass
  2. {
  3. ……类的名称等一切信息……
  4. CRuntimeClass * m_pBaseClass;//指向所属的基类。
  5. CRuntimeClass * m_pNextClass;//定义表尾的时候只要定义此指针为空就可以 了。
  6. static CRuntimeClass* pFirstClass;//这里表头指针属于静态变量,因为我们知道static变量在内存中只初始化一次,就可以为各对象所用!保证了各对象只有一个表头。
  7. };

有了CRuntimeClass结构后,我们就可以定义链表了:

C++代码
  1. static CRuntimeClass classCObject={NULL,NULL};  //这里定义了一个CRuntimeClass对象,因为classCObject无基类,所以m_pBaseClass为NULL。因为目前只有一个元素(即目前没有下一元素),所以m_pNextClass为NULL(表尾)。

至于pFirstClass(表头),大家可能有点想不通,它到什么地方去了。因为我们这里并不想把classCObject作为链表表头,我们还要在前面插入很多的CRuntimeClass对象,并且因为pFirstClass为static指针,即是说它不是属于某个对象,所以我们在用它之前要先初始化:CRuntimeClass* CRuntimeClass::pFirstClass=NULL;。

现在我们可以在前面插入一个CRuntimeClass对象,插入之前我得重要申明一下:如果单纯为了运行时类型识别,我们未必用到m_pNextClass指针(更多是在运行时创建时用),我们关心的是类本身和它的基类。这样,查找一个对象是否属于一个类时,主要关心的是类本身及它的基类。

C++代码
  1. CRuntimeClass classCCmdTarget={ &classCObject, NULL};
  2. CRuntimeClass classCWnd={ &classCCmdTarget ,NULL };
  3. CRuntimeClass classCView={ &classCWnd , NULL };

好了,上面只是仅仅为一个指针m_pBaseClass赋值(MFC中真正CRuntimeClass有多个成员变量和方法),就连接成了链表。假设我们现在已全部构造完成自己需要的CRuntimeClass对象,那么,这时候应该定义表头。即要用pFirstClass指针指向我们最后构造的CRuntimeClass对象——classCView。

C++代码
  1. CRuntimeClass::pFirstClass=&classCView;

现在链表有了,表头表尾都完善了,问题又出现了,我们应该怎样访问每一个CRuntimeClass对象?要判断一个对象属于某类,我们要从表头开始,一直向表尾查找到表尾,然后才能比较得出结果吗。肯定不是这样!

类中构造CRuntimeClass对象

大家可以这样想一下,我们构造这个链表的目的,就是构造完之后,能够按主观地拿一个CRuntimeClass对象和链表中的元素作比较,看看其中一个对象是否属于你指定的类。这样,我们需要有一个函数,一个能返回自身类型名的函数GetRuntimeClass()。

上面简单地说了一下链表的过程,但单纯有这个链表是没有任何意义。回到MFC中来,我们要实现的是在每个需要有RTTI能力的类中构造一个CRuntimeClass对象,比较一个类是否属于某个CRuntimeClass对象的时候,实际上只是比较CRuntimeClass对象。

如何在各个类之中插入CRuntimeClass对象,并且指定CRuntimeClass对象的内容及CRuntimeClass对象的链接,这里起码有十行的代码才能完成。在每个需要有RTTI能力的类设计中都要重复那十多行代码是一件乏味的事情,也容易出错,所以MFC用了两个宏代替这些工作,即DECLARE_DYNAMIC(类名)和IMPLEMENT_DYNAMIC(类名,基类名)。从这两个宏我们可以看出在MFC名类中的CRuntimeClass对象构造连接只有类名及基类名的不同!

到此,可能会有朋友问:为什么要用两个宏,用一个宏不可以代换CRuntimeClass对象构造连接吗?个人认为肯定可以,因为宏只是文字代换的游戏而已。但我们在编程之中,头文件与源文件是分开的,我们要在头文件头声明变量及方法,在源文件里实具体实现。即是说我们要在头文件中声明:

C++代码
  1. public:
  2. static CRuntimeClass classXXX  //XXX为类名
  3. virtual CRuntime* GetRuntimeClass() const;

然后在源文件里实现:

C++代码
  1. CRuntimeClass* XXX::classXXX={……};
  2. CRuntime* GetRuntimeClass() const;
  3. { return &XXX:: classXXX;}//这里不能直接返回&classXXX,因为static变量是类拥有而不是对象拥有。

我们一眼可以看出MFC中的DECLARE_DYNAMIC(类名)宏应该这样定义:

C++代码
  1. #define DECLARE_DYNAMIC(class_name) public: static CRuntimeClass class##class_name; virtual CRuntimeClass* GetRuntimeClass() const;

其中##为连接符,可以让我们传入的类名前面加上class,否则跟原类同名,大家会知道产生什么后果。

有了上面的DECLARE_DYNAMIC(类名)宏之后,我们在头文件里写上一句

DECLARE_DYNAMIC(XXX)

宏展开后就有了我们想要的:

public:
             static CRuntimeClass classXXX  //XXX为类名
             virtual CRuntime* GetRuntimeClass() const;

对于IMPLEMENT_DYNAMIC(类名,基类名),看来也不值得在这里代换文字了,大家知道它是知道回事,宏展开后为我们做了什么,再深究真是一点意义都没有!
有了此链表之后,就像有了一张存放各类型的网,我们可以轻而易举地RTTI。

IsKindOf函数

CObject有一个函数BOOL IsKindOf(const CRuntimeClass* pClass) const;,被它以下所有派生类继承。

此函数实现如下:

C++代码
  1. BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
  2. {
  3. CRuntimeClass* pClassThis=GetRuntimeClass();//获得自己的CRuntimeClass对象指针。
  4. while(pClassThis!=NULL)
  5. {
  6. if(pClassThis==pClass) return TRUE;
  7. pClassThis=pClassThis->m_pBaseClass;//这句最关键,指向自己基类,再回头比较,一直到尽头m_pBaseClass为NULL结束。
  8. }
  9. return FALSE;
  10. }

说到这里,运行时类型识别(RTTI)算是完成了。

MFC六大核心机制之二:运行时类型识别(RTTI)相关推荐

  1. VC6启用运行时类型识别 (RTTI)

    在程序中,当我们对多态类的基类指针使用typeid,就可以在运行时确定指针指向对象的实际类型,并输出对象类型的名字. #include  < cstdlib >  #include  &l ...

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

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

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

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

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

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

  5. MFC六大核心机制之一:MFC程序的初始化

    很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识就可以做出各种窗口程序,但喜欢钻研的朋友肯定想知道,到底微软帮我们做了些什么,让我们在它的框架下 ...

  6. MFC六大核心机制之一MFC程序的初始化

    原文地址: http://www.jizhuomi.com/software/267.html 很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识 ...

  7. 如何优雅的写UI——(1)MFC六大核心机制-程序初始化

    很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识就可以做出各种窗口程序,但喜欢钻研的朋友肯定想知道,到底微软帮我们做了些什么,让我们在它的框架下 ...

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

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

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

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

最新文章

  1. 镭波笔记本安装linux,镭波笔记本windows7旗舰版系统下载与安装教程
  2. 前端学习(2180):vue-router全局导航守卫
  3. ajaxFileUpload+struts2多文件上传(动态添加文件上传框)
  4. 一个工作三年左右的Java程序员跟大家分享从业心得
  5. mocha 的基本介绍expect风格断言库的基本语法
  6. 在eclipse中建立java项目
  7. 小米3g刷高格固件_小米路由器3g刷第三方固件遇到的一些坑
  8. 路径规划基本介绍(一)
  9. AutoCAD 2000-14的序列号大全
  10. 库存管理中的 (s, S) 策略,K convex,K 凸
  11. Voldemort的FailureDetector设计
  12. PHP几大框架的优点与缺点
  13. qtxlsx编译报错_QtXlsx编译错误*** [Makefile:37: sub-xlsx-qmake_all] Error 2
  14. 固定摄像头检测行人投射到gps坐标
  15. 【台大郭彦甫】Matlab入门教程超详细学习笔记五:初阶绘图(附PPT链接)
  16. 入门|一文读懂区块链“谷歌引擎”Covalent API, 无需编程实现跨链技术只需一秒
  17. 计算机科学应用论文题目,比较好写的计算机科学与应用论文题目 计算机科学与应用论文题目怎么取...
  18. Prolog系列学习-1
  19. 计算机三级网络技术(重点)
  20. 纯CSS+HTML绘制小猪佩奇---泥腿子前端出品

热门文章

  1. MapReduce-流量统计求和-Reducer和JobMain代码编写
  2. 常用的分布式事务解决方案介绍
  3. mysql的索引介绍_1
  4. arduino编译失败_Arduino编译时失败
  5. 最短路径 - 迪杰斯特拉(Dijkstra)算法
  6. 网站安装打包 软件环境检测与安装[二] 下
  7. GridView实战二:使用ObjectDataSource数据源控件(自定义缓存机制实现Sort)
  8. 使用装饰器时带括号与不带括号的区别
  9. Reporting Service 2012 体系结构
  10. 第五章 MongoDb索引优化 5.6