RTTI(运行时类型识别)--- 类别型录网与CRuntimeClass --- 令人拍案叫绝的宏
CRuntimeClass是一个结构,一个(类别型录网的)节点。记录类信息、类之间的连接及其他一些信息。MFC中使每个类中都有一个CRuntimeClass成员变量。
类别型录网是一个链表结构。有了它,就可以为RTTI、Dynamic
Creation、Message
Mapping、Command
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_pBaseClass和m_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_DYNCREATE和IMPLEMENT_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_SERIAL和IMPLEMENT_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还需要两个函数Load和Store,故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_PAINT、ON_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。