Dissecting MFC 总结(2)

Posted on 2008-02-26 19:05 蓝色山茶 阅读(200) 评论(0)  编辑 收藏 引用

RTTI(运行时类型识别)--- 类别型录网与CRuntimeClass --- 令人拍案叫绝的宏

 

CRuntimeClass是一个结构,一个(类别型录网的)节点。记录类信息、类之间的连接及其他一些信息。MFC中使每个类中都有一个CRuntimeClass成员变量。

类别型录网是一个链表结构。有了它,就可以为RTTIDynamic CreationMessage MappingCommand Routing服务!

CRuntimeClass

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct CRuntimeClass

{

//Attributes

       LPCSTR m_lpszClassName;

       int m_nObjectSize;

       UINT m_wSchema;                     //schema number of the loaded class

       CObject *(PASCAL *m_pfnCreateObject)();              //NULL => abstract class

       CRuntimeClass *m_pBaseClass;

 

//CRuntimeClass objects linked together in simple list

       static CRuntimeClass *pFirstClass;                     //start of class list

       CRuntimeClass *m_pNextClass;                         //linked list of registered classes

};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CRuntimeClass巧妙的加入类中,并取得其地址。用DECLARE_DYNAMIC

DECLARE_DYNAMIC

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define DECLARE_DYNAMIC(class_name) \

public: \

       static CRuntimeClass class##class_name; \

       virtual CRuntimeClass *GetRuntimeClass() const;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

类型别录的内容(也就是各个CRuntimeClass对象内容)的指定,以及连接工作:使用IMPLEMENT_DYNAMIC

IMPLEMENT_DYNAMIC

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \

       _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

其中的_IMPLEMENT_RUNTIMECLASS又是一个宏,这样区分是因为这个宏在“动态创建”时仍会用到(见下篇)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#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; }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       其中又有RUNTIME_CLASS宏,定义如下:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       #define RUNTIME_CLASS(class_name) \

              (&class_name::class##class_name)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       看起来整个IMPLEMENT_DYNAMIC内容好像只是指定初值,其实不然,其美妙之处在于它所使用的AFX_CLASSINIT,这是一个struct,并有一个构造函数,定义如下:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       struct AFX_CLASSINIT

       {

              AFX_CLASSINIT(CRuntimeClass *pNewClass);

       };

       AFX_CLASSINIT::AFX_CLASSINIT(CruntimeClass *pNewClass)

       {

              pNewClass->m_pNextClass = CruntimeClass::pFirstClass;

              CruntimeClass::pFirstClass = pNewClass;

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       很明显,此构造函数负责linked list 的连接工作。

       于是乎,程序中只需两个宏,就完成建构数据(CRuntimeClass)并加入(类别型录网)链表的工作。(即可为以后之动态创建及消息传递服务)。

       注意:链表的头(CObject),由于m_pBaseClassm_pNextClass为空,需特别设计:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       //in head file

class Cobject

{

public:

       virtual CRuntimeClass *GetRuntimeClass() const;

      

public:

static CRuntimeClass classCObject;

};

//in implementation file

static char szCObject[] = “CObject”;

struct CRuntimeClass CObject::classCObject =

       { szCObject, sizeof(CObject), 0xffff, NULL, NULL, NULL };

static AFX_CLASSINIT _init_CObject(&CObject::classCObject);

CRuntimeClass *CObject::GetRuntimeClass() const

{

       return &CObject::classCObject;

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

并且将CRuntimeClass中的static成员变量初始化。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//in implementation file

CRuntimeClass *CRuntimeClass::pFirstClass = NULL;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

至此,“类别型录”链表的头部也形成了。

 

注:宏定义之中##用来告诉编译器把两个字符串系在一起。

 

 

Dynamic Creation(动态创建)

 

在类别型录网的基础上,只要在CRuntimeClass结构中加入类的大小及建构函数(非类的构造函数)即可。

类别型录网中元素CRuntimeClass修改如下:

CRuntimeClass

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct CRuntimeClass

{

//Attributes

       LPCSTR m_lpszClassName;

       int m_nObjectSize;

       UINT m_wSchema;                     //schema number of the loaded class

       CObject *(PASCAL *m_pfnCreateObject)();              //NULL => abstract class

       CRuntimeClass *m_pBaseClass;

      

       CObject *CreateObject();

       static CRuntimeClass *PASCAL Load();

 

//CRuntimeClass objects linked together in simple list

       static CRuntimeClass *pFirstClass;                     //start of class list

       CRuntimeClass *m_pNextClass;                         //linked list of registered classes

};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       同时也将两个宏也修改成DECLARE_DYNCREATEIMPLEMENT_DYNCREATE,这两个宏涵盖上两个宏,也即包含了其功能。

       DECLARE_DYNCREATE

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       #define DECLARE_DYNCREATE(class_name) \

              DECLARE_DYNAMIC(class_name) \

              static CObject *PASCAL CreateObject();

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       IMPLEMENT_DYNCREATE

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \

       CObject *PASCAL class_name::CreateObject() \

              { return new class_name; } \

       _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xffff, \

class_name::CreateObject)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       因此,程序在执行期获得一个类名称,它就可以在“类别型录网”中找出对应的元素,然后调用其建构函数(不是指C++中的构造函数),产生出对象。

 

 

Persistence机制---读写文件

 

       MFC有一套Serialize机制,把文件名的选择、文件的开关、缓冲区的建立、数据的读写、提取运算符(>>)和插入运算符(<<)的重载(overload)、对象的动态创建等都包装了起来。将Serialize函数神不知鬼不觉的放入类声明当中就能使类进行文件读写的操作。(前提,是拥有动态创建的能力,故下面两个宏又涵盖上面两个宏)。

       MFC使用两个宏:DECLARE_SERIALIMPLEMENT_SERIAL

DECLARE_SERIAL

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define DECLARE_SERIAL (class_name) \

DECLARE_DYNCREATE(class_name) \

friend CArchive &AFXAPI operator>>(CArchive &ar, class_name *&pOb);

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

IMPLEMENT_SERIAL

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define IMPLEMENT_SERIAL (class_name, base_class_name, wSchema) \

       CObject *PASCAL class_name::CreateObject() \

              { return new class_name; } \

       _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \

class_name::CreateObject) \

              CArchive &AFXAPI operator>>(CArchive &ar, class_name *&pOb) \

                     { pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \

return ar; }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

       为了在每一个对象被处理(读或写)之前,能够处理琐屑的事情,诸如判断是否第一次出现、记录版本号码、记录文件名等工作,CRuntimeClass还需要两个函数LoadStore,故CRuntimeClass再次修改为:

CRuntimeClass

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct CRuntimeClass

{

//Attributes

       LPCSTR m_lpszClassName;

       int m_nObjectSize;

       UINT m_wSchema;                     //schema number of the loaded class

       CObject *(PASCAL *m_pfnCreateObject)();              //NULL => abstract class

       CRuntimeClass *m_pBaseClass;

      

       CObject *CreateObject();

       void Store(CArchive &ar) const;

       static CRuntimeClass *PASCAL Load(CArchive &ar, UINT *pwSchemaNum);

 

//CRuntimeClass objects linked together in simple list

       static CRuntimeClass *pFirstClass;                     //start of class list

       CRuntimeClass *m_pNextClass;                         //linked list of registered classes

};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

 

Message Mapping(消息映射)---消息映射表的建构(message map

 

       消息映射表是一个数据结构。组成了整个消息传递网。MFC中也是由宏建构,定义如下:

DECLARE_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define DECLARE_MESSAGE_MAP() \

       static AFX_MESSAGE_ENTRY _messageEntries[]; \

       static AFX_MSGMAP messageMap; \

       virtual AFX_MSGMAP *GetMessageMap() const;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

其中AFX_MSGMAP是一个数据结构:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct AFX_MSGMAP

{

       AFX_MSGMAP *pBaseMessageMap;

       AFX_MSGMAP_ENTRY *lpEntries;

};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

其中的AFX_MSGMAP_ENTRY又是另一个数据结构:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct AFX_MSGMAP_ENTRY    //MFC 4.0 format

{

       UINT nMessage;           //windows message

       UINT nCode;                //control code or WM_NOTIFY code

       UINT nID;                   //control ID (or 0 for windows messages)

       UINT nLastID;              //used for entries specifying a range of control id’s

       UINT nSig;                  //signature type (action) or pointer to message #

       AFX_PMSG pfn;          //routine to call (or special value)

};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

其中的AFX_PMSG定义为函数指针:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

typedef void (CCmdTatget::*AFX_PMSG) (void);

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

这个数据结构内容的填塞工作由三个宏完成,分别定义如下:

BEGIN_MESSAGE_MAP

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \

       AFX_MSGMAP *theClass::GetMessageMap() const \

              { return &theClass::messageMap; } \

       AFX_MSGMAP theClass::messageMap = \

       { &(baseClass::messageMap), \

              (AFX_MSGMAP_ENTRY*) &(theClass::_messageEntries) }; \

       AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \

       {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define ON_COMMAND(id, memberFxn) \

       { WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, \

(AFX_PMSG)memberFxn },

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define END_MESSAGE_MAP() \

       { 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \

       };

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

其中的AfxSig_end定义为:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

enum AfxSig_end

{

       AfxSig_end = 0,                  //[marks end of message map]

       AfxSig_vv,

};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

还可以定义各种类似ON_COMMAND这样的宏,把各式各样的消息与特定的处理程序关联起来。MFC里就有如:ON_WM_PAINTON_WM_CREATE等宏。

 

 

Command Routing(命令传递)

 

       MFC对于消息循环的规定是:

       1. 如果是一般的Windows消息(WM_xxx),则一定是由派生类流向基类,没有旁流的可能。(把消息往父类推比较每个类的消息映射表,成功则调用相应的处理函数)

       2. 如果是命令消息WM_COMMAND,则可能旁流。

       WM_COMMAND消息传递路线,根据侯老师:

       view对象发出:

       1221       CMyView

       122         CView

       12           CWnd

       1            CCmdTarget

       131         CMyDoc

       13           CDocument

       1            CcmdTarget

 

       frame对象发出:

       1221       CMyView

       122         CView

       12           CWnd

       1            CCmdTarget

       131         CMyDoc

       13           CDocument

       1            CCmdTarget

       1211              CMyFrame

       121         CFrameWnd

       12           CWnd

       1            CCmdTarget

       1111        CMyWinApp

       111         CWinApp

       1            CcmdTarget

 

最后仍未找到,则退回到CWnd::WindowProc,调用 CWnd::DefWindowProc,此函数则调用::DefWindowProc



只有注册用户登录后才能发表评论。
网站导航:   博客园   博客园最新博文   博问   管理


posts - 1, comments - 0, trackbacks - 0, articles - 4

Copyright © 蓝色山茶