MFC六大关键技术之剖析(第二部分)

二、运行时类型识别(RTTI)

运行时类型识别(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要做些什么事情。就好像我们在一个新型(新型到我们还不认识)电器公司里面,我们要识别哪个是电饭锅,哪个是电磁炉等等,我们要查看登记的各电器一系列的信息,我们才可以比较、鉴别,那个东西是什么!

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

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

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对象和链表中的元素作比较,看看其中一个对象中否属于你指定的类。这样,我们需要有一个函数,一个能返回自身类型名的函数GetRuntimeClass()。

上面简单地说一下链表的过程,但单纯有这个链表是没有任何意义。回到MFC中来,我们要实现的是在每个需要有RTTI能力的类中构造一个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。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; 
}

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/liuliye23/archive/2009/12/15/5008657.aspx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《深入浅出mfc 第2版》是一本介绍使用MFC(Microsoft Foundation Class)框架进行Windows程序开发的书籍。以下是该书第2版的目录: 第一部分MFC基础知识 第1章:MFC概述 本章主要介绍了MFC框架的基本概念和结构,包括应用程序、文档、视图、框架窗口等核心组件的作用和关系。 第2章:MFC应用程序框架 该章详细介绍了MFC应用程序框架的创建和使用方法,包括应用程序类的创建、初始化、消息循环等内容。 第3章:MFC文档和视图 本章介绍了MFC中的文档和视图类的概念和使用方法,以及如何在框架中创建多文档和单文档应用程序。 第4章:MFC对话框和控件 该章讲解了MFC中对话框的创建和使用,包括控件的添加、消息响应等内容。 第二部分MFC高级应用 第5章:MFC菜单和工具栏 本章介绍了如何在MFC应用程序中创建和使用菜单和工具栏,以及如何添加图标和快捷键等。 第6章:MFC绘图和图像处理 该章详细介绍了MFC中的绘图和图像处理功能,包括直线、曲线、矩形、椭圆等的绘制方法。 第7章:MFC文件和目录操作 本章讲解了在MFC应用程序中如何进行文件和目录操作,包括文件的打开、读写、保存等功能。 第8章:MFC多线程编程 该章介绍了MFC中的多线程编程方法,包括创建和使用线程、线程同步等内容。 第三部分MFC实战开发 第9章:MFC数据库编程 本章详细介绍了MFC中的数据库编程,包括连接数据库、查询、插入、更新、删除等操作。 第10章:MFC网络编程 该章讲解了MFC中的网络编程方法,包括创建和使用套接字、实现客户端和服务器端的通信等内容。 第11章:MFC图形用户界面设计 本章介绍了如何使用MFC框架进行图形用户界面的设计,包括窗口布局、控件属性设置、菜单和工具栏的使用等。 第12章:MFC应用程序发布与部署 该章讲解了如何将开发完成的MFC应用程序进行发布和部署,以便在其他计算机上运行。 通过阅读《深入浅出mfc 第2版》的目录,读者可以全面了解MFC框架和其在Windows程序开发中的应用,从基础知识到高级应用再到实战开发,逐步掌握MFC的使用技巧和开发经验。这本书对于想要学习和掌握MFC框架的开发者来说是一本很有价值的参考资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值