MFC深入浅出之RTTI技术---宏的定义

来源:互联网 发布:sql exists 怎么用 编辑:程序博客网 时间:2024/06/04 20:02
 

最近在学习控件自绘,发现MFC的内部很多东西还不是很了解,所以现在开始看MFC深入浅出,其中要学习的主要是消息传递.对于D-V.暂时不学.为了使得MFC类库的一系列具有RTTI(也就是运行时类型识别)特性.这样需要外加一个结构来达到这个功能,但是这个结构又要有很好的封装性,所以自然而然的想到宏.其实宏有很多好的,也可以成为高级特性.市场上的书籍是很少介绍到这方面的知识的.究其原因,不外乎就是因为它和goto一样使得程序难以理解,并且可能破坏程序的结构.

#define DECLARE_DYNAMIC( class_name ) \

public:\

static CRuntimeClass class##class_name;\

virtual CRutimeClass* GetRuntimeClass() const;

这个宏大致是定义了两个成员(一个数据成员class##class_name和获取该数据成员的函数).

其中需要解释两点:

1. 关于"\",这个理解起来就是连接符号.因为宏的定义通常很长,如果把它写在一行会造成程序的难以阅读.所以采用了这种容易理解的格式.

2. 关于##:也是一个连接符号,这个和前面说的"\"有点不一样.其实也差不了多少.他就是把两个字符串直接连接起来.关于为什么会有这个东西,说一点我的理解:因为我们在定义符号的时候是需要和宏的参数名相关的变量的.为了表示这个由本身和参数连体表示的变量,所以采用这种表示.以教材为例,这就就相当于:static CRuntimeClass classCView;

#define IMPLEMENT_DYNAMIC( class_name,base_class_name ) \

_IMPLEMENT_RUNTIMECLASS( class_name,base_class_name,0xFFFF,NULL )

#define _IMPLEMENT_RUNTIMECLASS( class_name,base_class_name,wSchema,pfnNew ) \

static char _lpsz##class_name[] = #class_name; \

CRuntimeClass class_name::class##class_name = { \

//这里就是一些初始化工作

_lpsz##class_name,sizeof( class_name ),wSchema,pfnNew, \

RUNTIME_CLASS( base_class_name ),NULL };\

static AFX_CLASSINIT _init_#class_name( &class_name::class##class_name )

CRuntimeClass* class_name::GetRuntimeClass() const \

{ return &class_name::class##class_name; } \

其中RUTIME_CLASS定义如下:

#define RUNTIME_CLASS( class_name ) \

( &class_name::class##class_name )

其中AFX_CLASSINIT结构体定义如下:

struct AFX_CLASSINIT

{

AFX_CLASSINIT( CRuntimeClass* pNewClass )

{

pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;

CRuntimeClass::pFirstClass = pNewClass;

}

};

需要解释有以下几点:

1. "#":在宏里面表示后面所跟的是字符串.(即如果有#define AFX(x)  #x则表示AFX="x")

2. static AFX_CLASSINIT _init_#class_name( &class_name::class##class_name );

这里是定义一个成员变量(而非定义一个返回值为AFX_CLASSINIT的函数).但是这个结构体很特殊,特殊的它只有构造函数,而且更加不可思议的是这个构造函数就是为了构造链表而存在的.我现在把实参传入以后,结果如下:

AFX_CLASSINIT::AFX_CLASSINIT(&class_name::class##class_name)

{

//把当前节点的next指向已经连接好的first

class_name::class##class_name.m_pNextClass = CRuntimeClass::pFirstClass;

//改变first指针,使其指向本身.

CRutimeClass::pFirstClass = pNewClass;

}

整个过程如下:

分析完了两个宏,接着就是分析Frame3里面的源码.

看完前面的两个宏,我想你有一些地方还是有点模棱两可吧!

首先是数据从哪来,在哪里用到,咋看一遍代码,发现没有那个地方产生CView等等对象(当然,对于CMyWinAppCMyFrameWnd,代码里面却是是产生了对象,但是这里的对象产生在这完全没有意义了,这个应该是在Frame2里面就讨论到的流程).

那么数据流CRunTimeClass从哪来?

这确实是个好问题.我们再去看这个继承类(CObject.CCmdTarget...)的结构里面.发现一个很重要也是共性的问题就是所有的对象都是作为继承类(CObject.CCmdTarget...)里面此对象都是静态成员.而我们知道,静态成员基本和类的对象"不沾边".现在我们可以这样理解数据,他其实是产生了一系列的数据(CRuntimeClass的一系列对象),而区分这些对象的一个重要"指标"(或特征)就是这个对象里面m_lpszClassName成员.有这个成员和继承类相关联.

这样,我们在PrintAllClasses函数里面就可以通过pFirstClassm_pNextClass来访问他们了.

还有一点需要说明的是,IMPLEMENT_DYNAMIC定义的次序,决定了由m_pNextClass的顺序.