工具软件   办公软件   操作系统   网络安全   设计在线   程序开发   教程宝典   软件下载   软件论坛
您的位置:软件 > 开发者网络 > 开发工具 > 开发专栏 > VC > 正文
MFC六大关键技术之运行时类型识别
[文章信息]
作者:小李先生
时间:2005-03-01
出处:blog
责任编辑:方舟
[文章导读]
运行时类型识别(RTTI)即是程序执行过程中知道某个对象属于某个类,我们平时用C++编程接触的RTTI一般是编译器的RTTI
advertisement
热点推荐
· 编写通用的ASP防SQL注入攻击程序
· 备份精灵Ghost问答集锦
· MSN Messenger新版功能截图
· 设计欣赏:《夜精灵》之最后的释然
· Word表格数据与文本的相互转换
[正文]
  运行时类型识别(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;
}

  说到这里,运行时类型识别(RTTI)算是完成了。写这篇文章的时候,我一直重感冒。我曾一度在想,究竟写这东西是为了什么。因为如果我把这些时间用在别的地方(甚至帮别人打打字),应该有数百元的报酬。

  是什么让“嗜财如命”的我继续写下去?我想,无非是想交几个计算机的朋友而已。计算机是大家公认高科技的东西,但学习它的朋友大多只能默默无闻,外界的朋友也不知道怎么去认识你。程序员更不是“潮流”的东西,更加得不到别人的认可。

  有一件个人认为很典型的事情,有一天,我跟一个朋友到一个单位里面。里面有一个女打字员。朋友看着她熟练的指法,心悦诚服地说:“她的电脑水平比你的又高了一个很高的层次!”,那个女的打字高手亦自豪地说:“我靠电脑为生,电脑水平肯定比你(指笔者)的好一点!换着是你,如果以电脑为生,我也不敢说好过你!”。虽然我想声明我是计算机专业的,但我知道没有理解,所以我只得客气地点头。

  选择电脑“潮流”的东西实际是选择了平凡,而选择做程序员就是选择了孤独!幸好我不是一个专门的程序员,但即使如此,我愿意做你们的朋友,因为我爱你们!

天极社区邀请您:写博客日记  上传相片   论坛聊天  订阅电子杂志  推荐网摘   免费图铃工具
笔名:   请您注意:

 遵守国家有关法律、法规,尊重网上道德,承担一切因您的行为而直接或间接引起的法律责任。

 天极网拥有管理笔名和留言的一切权利。
评论:
 
发表评论推荐给朋友我想参加相关培训打印我对此感兴趣订阅电子杂志
相关内容焦点新闻
  • 轻松实现Visual C++源代码规范整理
  • 用Visual C++实现CPU特权指令操作
  • VC下用串口与电台通信需注意的问题
  • 剖析MFC六大关键技术之初始化过程
  • VC调用ACM音频编程接口压缩Wave音频
  • 中国几乎所有少数民族文字都能运行Windows
  • 据传TOM在线将收购阿里巴巴或与之合并
  • 法律层面解决电信互联互通 获实质进展
  • 我国电话用户已达6.55亿 手机用户有3.4亿
  • 北京EVD跌至900元以下 一年价格下降50%
  • 依靠信息化 东北老工业基地能否再次腾飞?
  • 企业如何做好“十一五”信息化的目标规划
  • WAPI再遭不公对待 学者建议先做强制标准
  • Advertisement

    天极无线


    奇妙科幻|美好风光|清风车影|漫画卡通|星座生肖|明星写真|动物世界
    老鼠爱大米
    挥着翅膀的女孩
    女人味
    栀子花开
    白月光
    刚刚好
    江南
    快乐崇拜
    亲爱的你怎么不在我身边
    小薇
    2002年的第一场雪
    有多少爱可以重来
    我的地盘
    七里香
    情人
     
    老鼠爱大米 老板电话
    冲动的惩罚 七里香
    我不是黄蓉 女生撒娇
    盛夏的果实 坚持到底
    孤单北半球 眉飞色舞
    挪威的森林 可爱女人
    最浪漫的事 老板电话

    CSEEK搜索