MFC六大核心机制之二:运行时类型识别(RTTI)
上一节讲的是MFC六大核心机制之一:MFC程序的初始化,本节继续讲解MFC六大核心机制之二:运行时类型识别(RTTI)。
typeid运算子
运行时类型识别(RTTI)即是程序执行过程中知道某个对象属于某个类,我们平时用C++编程接触的RTTI一般是编译器的RTTI,即是在新版本的VC++编译器里面选用“使能RTTI”,然后载入typeinfo.h文件,就可以使用一个叫typeid()的运算子,它的地位与在C++编程中的sizeof()运算子类似的地方(包含一个头文件,然后就有一个熟悉好用的函数)。typdid()关键的地方是可以接受两个类型的参数:一个是类名称,一个是对象指针。所以我们判别一个对象是否属于某个类就可以象下面那样:
- if (typeid (ClassName)== typeid(*ObjectName)){
- ((ClassName*)ObjectName)->Fun();
- }
像上面所说的那样,一个typeid()运算子就可以轻松地识别一个对象是否属于某一个类,但MFC并不是用typeid()的运算子来进行动态类型识别,而是用一大堆令人费解的宏。很多学员在这里很疑惑,好象MFC在大部分地方都是故作神秘。使们大家编程时很迷惘,只知道在这里加入一组宏,又在那儿加入一个映射,而不知道我们为什么要加入这些东东。
其实,早期的MFC并没有typeid()运算子,所以只能沿用一个老办法。我们甚至可以想象一下,如果MFC早期就有template(模板)的概念,可能更容易解决RTTI问题。
所以,我们要回到“古老”的年代,想象一下,要完成RTTI要做些什么事情。就好像我们在一个新型(新型到我们还不认识)电器公司里面,我们要识别哪个是电饭锅,哪个是电磁炉等等,我们要查看登记的各电器一系列的信息,我们才可以比较、鉴别,那个东西是什么!
CRuntimeClass链表的设计
要登记一系列的消息并不是一件简单的事情,大家可能首先想到用数组登记对象。但如果用数组,我们要定义多大的数组才好呢,大了浪费空间,小了更加不行。所以我们要用另一种数据结构——链表。因为链表理论上可大可小,可以无限扩展。
链表是一种常用的数据结构,简单地说,它是在一个对象里面保存了指向下一个同类型对象的指针。我们大体可以这样设计我们的类:
- struct CRuntimeClass
- {
- ……类的名称等一切信息……
- CRuntimeClass * m_pNextClass;//指向链表中下一CRuntimeClass对象的指针
- };
链表还应该有一个表头和一个表尾,这样我们在查链表中各对象元素的信息的时候才知道从哪里查起,到哪儿结束。我们还要注明本身是由哪能个类派生。所以我们的链表类要这样设计:
- struct CRuntimeClass
- {
- ……类的名称等一切信息……
- CRuntimeClass * m_pBaseClass;//指向所属的基类。
- CRuntimeClass * m_pNextClass;//定义表尾的时候只要定义此指针为空就可以 了。
- static CRuntimeClass* pFirstClass;//这里表头指针属于静态变量,因为我们知道static变量在内存中只初始化一次,就可以为各对象所用!保证了各对象只有一个表头。
- };
有了CRuntimeClass结构后,我们就可以定义链表了:
- 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指针(更多是在运行时创建时用),我们关心的是类本身和它的基类。这样,查找一个对象是否属于一个类时,主要关心的是类本身及它的基类。
- CRuntimeClass classCCmdTarget={ &classCObject, NULL};
- CRuntimeClass classCWnd={ &classCCmdTarget ,NULL };
- CRuntimeClass classCView={ &classCWnd , NULL };
好了,上面只是仅仅为一个指针m_pBaseClass赋值(MFC中真正CRuntimeClass有多个成员变量和方法),就连接成了链表。假设我们现在已全部构造完成自己需要的CRuntimeClass对象,那么,这时候应该定义表头。即要用pFirstClass指针指向我们最后构造的CRuntimeClass对象——classCView。
- CRuntimeClass::pFirstClass=&classCView;
现在链表有了,表头表尾都完善了,问题又出现了,我们应该怎样访问每一个CRuntimeClass对象?要判断一个对象属于某类,我们要从表头开始,一直向表尾查找到表尾,然后才能比较得出结果吗。肯定不是这样!
类中构造CRuntimeClass对象
大家可以这样想一下,我们构造这个链表的目的,就是构造完之后,能够按主观地拿一个CRuntimeClass对象和链表中的元素作比较,看看其中一个对象是否属于你指定的类。这样,我们需要有一个函数,一个能返回自身类型名的函数GetRuntimeClass()。
上面简单地说了一下链表的过程,但单纯有这个链表是没有任何意义。回到MFC中来,我们要实现的是在每个需要有RTTI能力的类中构造一个CRuntimeClass对象,比较一个类是否属于某个CRuntimeClass对象的时候,实际上只是比较CRuntimeClass对象。
如何在各个类之中插入CRuntimeClass对象,并且指定CRuntimeClass对象的内容及CRuntimeClass对象的链接,这里起码有十行的代码才能完成。在每个需要有RTTI能力的类设计中都要重复那十多行代码是一件乏味的事情,也容易出错,所以MFC用了两个宏代替这些工作,即DECLARE_DYNAMIC(类名)和IMPLEMENT_DYNAMIC(类名,基类名)。从这两个宏我们可以看出在MFC名类中的CRuntimeClass对象构造连接只有类名及基类名的不同!
到此,可能会有朋友问:为什么要用两个宏,用一个宏不可以代换CRuntimeClass对象构造连接吗?个人认为肯定可以,因为宏只是文字代换的游戏而已。但我们在编程之中,头文件与源文件是分开的,我们要在头文件头声明变量及方法,在源文件里实具体实现。即是说我们要在头文件中声明:
- public:
- static CRuntimeClass classXXX //XXX为类名
- virtual CRuntime* GetRuntimeClass() const;
然后在源文件里实现:
- CRuntimeClass* XXX::classXXX={……};
- CRuntime* GetRuntimeClass() const;
- { return &XXX:: classXXX;}//这里不能直接返回&classXXX,因为static变量是类拥有而不是对象拥有。
我们一眼可以看出MFC中的DECLARE_DYNAMIC(类名)宏应该这样定义:
- #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;,被它以下所有派生类继承。
此函数实现如下:
- BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
- {
- CRuntimeClass* pClassThis=GetRuntimeClass();//获得自己的CRuntimeClass对象指针。
- while(pClassThis!=NULL)
- {
- if(pClassThis==pClass) return TRUE;
- pClassThis=pClassThis->m_pBaseClass;//这句最关键,指向自己基类,再回头比较,一直到尽头m_pBaseClass为NULL结束。
- }
- return FALSE;
- }
说到这里,运行时类型识别(RTTI)算是完成了。
MFC六大核心机制之二:运行时类型识别(RTTI)相关推荐
- VC6启用运行时类型识别 (RTTI)
在程序中,当我们对多态类的基类指针使用typeid,就可以在运行时确定指针指向对象的实际类型,并输出对象类型的名字. #include < cstdlib > #include &l ...
- java 运行时类型_Java基础之RTTI 运行时类型识别
运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于R ...
- 白话C++系列(27) -- RTTI:运行时类型识别
http://www.cnblogs.com/kkdd-2013/p/5601783.html RTTI-运行时类型识别 RTTI:Run-Time Type Identification. 那么RT ...
- C++ 学习笔记之(19) new、delete表达式、RTTI(运行时类型识别)、枚举、类成员指针、嵌套类、局部类、位域、volatile、extern C
C++ 学习笔记之(19) new.delete表达式.RTTI(运行时类型识别).枚举.类成员指针.嵌套类.局部类.位域.volatile.extern C C++ 学习笔记之(19) new.de ...
- MFC六大核心机制之一:MFC程序的初始化
很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识就可以做出各种窗口程序,但喜欢钻研的朋友肯定想知道,到底微软帮我们做了些什么,让我们在它的框架下 ...
- MFC六大核心机制之一MFC程序的初始化
原文地址: http://www.jizhuomi.com/software/267.html 很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识 ...
- 如何优雅的写UI——(1)MFC六大核心机制-程序初始化
很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识就可以做出各种窗口程序,但喜欢钻研的朋友肯定想知道,到底微软帮我们做了些什么,让我们在它的框架下 ...
- 深入浅出MFC学习笔记:MFC六大关键技术仿真之RTTI运行时类型识别
RTTI(运行时类型识别) 参考文献:深入浅出MFC-侯捷 怎样去构造类别型录网? 一.定义数据结构: 其中pFirstClass指针属于痊愈变量,所以它应该以static修饰之. 而且我们最终希望达 ...
- RTTI机制(运行时类型识别)
RTTI机制(运行时类型识别) 在多态里面,基类里的虚函数和派生类里的虚函数形成了遮蔽,这就导致在主程序运行时,有些表达式的类型没有办法确定.必须等到程序运行结束后,根据具体的环境才能确定.看下面的代 ...
最新文章
- 镭波笔记本安装linux,镭波笔记本windows7旗舰版系统下载与安装教程
- 前端学习(2180):vue-router全局导航守卫
- ajaxFileUpload+struts2多文件上传(动态添加文件上传框)
- 一个工作三年左右的Java程序员跟大家分享从业心得
- mocha 的基本介绍expect风格断言库的基本语法
- 在eclipse中建立java项目
- 小米3g刷高格固件_小米路由器3g刷第三方固件遇到的一些坑
- 路径规划基本介绍(一)
- AutoCAD 2000-14的序列号大全
- 库存管理中的 (s, S) 策略,K convex,K 凸
- Voldemort的FailureDetector设计
- PHP几大框架的优点与缺点
- qtxlsx编译报错_QtXlsx编译错误*** [Makefile:37: sub-xlsx-qmake_all] Error 2
- 固定摄像头检测行人投射到gps坐标
- 【台大郭彦甫】Matlab入门教程超详细学习笔记五:初阶绘图(附PPT链接)
- 入门|一文读懂区块链“谷歌引擎”Covalent API, 无需编程实现跨链技术只需一秒
- 计算机科学应用论文题目,比较好写的计算机科学与应用论文题目 计算机科学与应用论文题目怎么取...
- Prolog系列学习-1
- 计算机三级网络技术(重点)
- 纯CSS+HTML绘制小猪佩奇---泥腿子前端出品
热门文章
- MapReduce-流量统计求和-Reducer和JobMain代码编写
- 常用的分布式事务解决方案介绍
- mysql的索引介绍_1
- arduino编译失败_Arduino编译时失败
- 最短路径 - 迪杰斯特拉(Dijkstra)算法
- 网站安装打包 软件环境检测与安装[二] 下
- GridView实战二:使用ObjectDataSource数据源控件(自定义缓存机制实现Sort)
- 使用装饰器时带括号与不带括号的区别
- Reporting Service 2012 体系结构
- 第五章 MongoDb索引优化 5.6