正文: MFC执行期类型识别与动态创建技术是借助CRuntimeClass结构和一系列神秘的宏实现的。而动态创建技术 的前提是执行期类型识别网的建立。下面就让我们来看看执行期类型识别网是如何建立起来的? /// /*一. 1.CRuntimeClass结构总览 */ /// 注释:CRuntimeClass结构定义在../Visual Studio.NET/vc7/atlmfc/include/Afx.h中 // object type information 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 #ifdef _AFXDLL CRuntimeClass* (PASCAL* m_pfnGetBaseClass)(); #else CRuntimeClass* m_pBaseClass; #endif // Operations CObject* CreateObject(); BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// dynamic name lookup and creation static CRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);//for ANSI static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);//for Unicode static CObject* PASCAL CreateObject(LPCSTR lpszClassName); for ANSI static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);//for Unicode // Implementation void Store(CArchive& ar) const; static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum); // CRuntimeClass objects linked together in simple list CRuntimeClass* m_pNextClass; // linked list of registered classes const AFX_CLASSINIT* m_pClassInit; };
/ /* 一.2.执行期类型识别网组成部分 */ // 在CRuntimeClass结构中与执行期类型识别网建立有关的成员如下: struct CRuntimeClass { ...// LPCSTR m_lpszClassName; CRuntimeClass* m_pBaseClass; }
// /*一.3.执行期类型识别网的连接建立*/ // MFC在每一个具有执行期类型识别能力的类的.h和.cpp文件中都添加了两个宏。他们是: //in xx.h class class_name { DECLARE_DYNAMIC(class_name) ...// } //in xx.cpp IMPLEMENT_DYNAMIC(class_name, base_class_name) ...// 这两个宏展开后是什么样子呢?让我们看看其源代码吧! 注释:这两个宏定义在../Visual Studio.NET/vc7/atlmfc/include/Afx.h中 #define DECLARE_DYNAMIC(class_name) / public: / static const CRuntimeClass class##class_name; / virtual CRuntimeClass* GetRuntimeClass() const; /
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) / IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) / AFX_COMDAT const CRuntimeClass class_name::class##class_name = { / #class_name, sizeof(class class_name), wSchema, pfnNew, / RUNTIME_CLASS(base_class_name), NULL, class_init }; / CRuntimeClass* class_name::GetRuntimeClass() const / { return RUNTIME_CLASS(class_name); } / 相关宏定义如下: #define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name) #define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name)) 看清了吧!这两个宏互相配合着在每个类中都塞进了点东东,至于什么东东,自己看呗儿! 下面我以一个具体的类来展示以下执行期类型识别网是如何连接建立的。 以一个MDI应用程序的CMainFrame类为例: //in MaimFrm.h class CMainFrame : public CMDIFrameWnd { DECLARE_DYNAMIC(CMainFrame) ...// }
//in MaimFrm.cpp IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd) ...//
展开后得: //in MaimFrm.h class CMainFrame : public CMDIFrameWnd { public: static const CRuntimeClass classCMainFrame; virtual CRuntimeClass* GetRuntimeClass() const; ...// }
//in MaimFrm.cpp AFX_COMDAT const CRuntimeClass CMainFrame::classCMainFrame ={CMainFrame,sizeof(classCMainFrame ),0xFFFF,NULL,(CRuntimeClass*)(&CMDIFrameWnd::classCMDIFrameWnd),NULL,NULL};
CRuntimeClass* CMainFrame::GetRuntimeClass() const { return &CMainFrame::classCMainFrame; } 有上面的代码可以看到IMPLEMENT_DYNAMIC宏初始化和实现了DECLARE_DYNAMIC宏为CMainFrame添加的CRuntimeClass classCMainFrame和CRuntimeClass* GetRuntimeClass() const静态函数。 其中最重要的一点是: CMainFrame类的classCMainFrame对象的CRuntimeClass* m_pBaseClass被“间接的”赋值为&CMDIFrameWnd::classCMDIFrameWnd,也就是它基类的静态CRuntimeClass classCMDIFrameWnd成员。 以此类推一: CMDIFrameWnd::classCMDIFrameWnd的成员CRuntimeClass* m_pBaseClass被“间接的”赋值为 其基类&CFrameWnd::classCFrameWnd; .... .... CCmdTarget::classCCmdTarget的成员CRuntimeClass* m_pBaseClass被“间接的”赋值为&CObject::classCObject; CObject没有基类,那么CObject::classCObject的成员CRuntimeClass* m_pBaseClass被赋予什么值呢?看看what is the special runtime-class structure for CObject (no base class) in MFC Framework?
注释:CMainFrame类的基类顺序以此是CMDIFrameWnd--〉CFrameWnd-->CWnd-->CCmdTarget-->CObject
//in Afx.h // class CObject is the root of all compliant objects class AFX_NOVTABLE CObject { public: virtual CRuntimeClass* GetRuntimeClass() const; static const CRuntimeClass classCObject; ...// } //in objcore.cpp // special runtime-class structure for CObject (no base class) const struct CRuntimeClass CObject::classCObject = { "CObject", sizeof(CObject), 0xffff, NULL, NULL, NULL };
CRuntimeClass* CObject::GetRuntimeClass() const { return _RUNTIME_CLASS(CObject); } /// /* 一.4.执行期类型识别网的应用 */ // 执行期类型识别网建立好了,它将在MFC Framework中发挥重要作用,这里举一个其应用的例子: CObject::IsKindOf函数就是利用该网完成的函数。下面看看其源代码: //in objcore.cpp BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const { ASSERT(this != NULL); // it better be in valid memory, at least for CObject size ASSERT(AfxIsValidAddress(this, sizeof(CObject))); // simple SI case CRuntimeClass* pClassThis = GetRuntimeClass(); return pClassThis->IsDerivedFrom(pClass); } BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const { ASSERT(this != NULL); ASSERT(AfxIsValidAddress(this, sizeof(CRuntimeClass), FALSE)); ASSERT(pBaseClass != NULL); ASSERT(AfxIsValidAddress(pBaseClass, sizeof(CRuntimeClass), FALSE));
// simple SI case const CRuntimeClass* pClassThis = this; #ifdef _AFXDLL for (;;) #else while (pClassThis != NULL) #endif { if (pClassThis == pBaseClass) return TRUE; #ifdef _AFXDLL if (pClassThis->m_pfnGetBaseClass == NULL) break; pClassThis = (*pClassThis->m_pfnGetBaseClass)(); #else pClassThis = pClassThis->m_pBaseClass; #endif } return FALSE; // walked to the top, no match }
// /* 二.1. 执行期动态创建技术 */ // 谈完了执行期类型识别网建立后,就来看看另一项重要的技术-----执行期动态创建技术 ,该技术应用可谓是甚广,在你的SDI中的主框架,视图和其对应的文档以及MDI中的子框架,视图和其对应的文档都是利用动态创建技术生成的。 执行期动态创建技术利用的也是CRuntimeClass结构和一对对应的宏DECLARE_DYNCREATE(class_name) 和IMPLEMENT_DYNCREATE(class_name, base_class_name); 下面看看这两个宏的定义吧:
#define DECLARE_DYNCREATE(class_name) / DECLARE_DYNAMIC(class_name) / static CObject* PASCAL CreateObject(); #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, NULL) 应该看出来执行期类型识别网是执行期动态创建技术的基础。 /// /* 二.2 执行期动态创建技术组成部分*/ /// 下面将列出与执行期动态创建技术有关的CRuntimeClass的结构成员: // object type information struct CRuntimeClass { CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class CObject* CreateObject(); ...// }; /* 二.3.宏展开 */ 这回我们以SDI程序中的CMainFrame的动态创建为例,下面看看上面的两个宏在xx.h和xx.cpp文件中展开后的样子: //in MainFrm.h class CMainFrame { public: static const CRuntimeClass classCMainFrame; virtual CRuntimeClass* GetRuntimeClass() const; static CObject* PASCAL CreateObject(); ...// }; //in MainFrm.cpp CObject* PASCAL CMainFrame::CreateObject() { return new class_name; } AFX_COMDAT const CRuntimeClass CMainFrame::classCMainFrame ={ CMainFrame,sizeof(classCMainFrame),0xFFFF,CMainFrame::CreateObject,(CRuntimeClass*)(&CMDIFrameWnd::classCMDIFrameWnd),NULL,NULL};
CRuntimeClass* CMainFrame::GetRuntimeClass() const { return &CMainFrame::classCMainFrame; } 看出来着两个宏展开后与前面介绍的那两个宏的不同了吧;DECLARE_DYNCREATE在头文件中加入了一个函数static CObject* PASCAL CreateObject();IMPLEMENT_DYNCREATE的展开也有所不同。下面将详述。 /* 二.4.如何进行动态创建 */ /// 让我们看看CMainFrame的构造函数的access control吧!protected:疑惑了吧?受保护的构造函数是不能够得实例化的。那么CMainFrame是如何被构造出来的呢?这就是微软利用CRuntimeClass进行动态创建的原因。下面我用一个简单的例子来诠释一下构造方法: //in A.h class A { protected:A(){} ...//something else public:static A*CreateObject(); }; //in A.cpp A*A::CreateObject() { return new A();} //in test.cpp int main(void) { A*pNewObj=A::CreateObject();//OK return 0; } 诠释完了后就让我们一一对应的看看微软是如何做的吧,我们回头再看看DECLARE_DYNCREATE为CMainFrame 添加了什么吧。static CObject* PASCAL CreateObject(); 眼睛亮了吧!是的它就相当于上个例子中的 static A*CreateObject();那么是谁调用了它呢?再回头看看IMPLEMENT_DYNCREATE作了什么吧?
AFX_COMDAT const CRuntimeClass CMainFrame::classCMainFrame ={ CMainFrame,sizeof(classCMainFrame),0xFFFF,CMainFrame::CreateObject,(CRuntimeClass*)(&CFrameWnd::classFrameWnd),NULL,NULL}; 原来该宏把m_pfnCreateObject赋值为CMainFrame::CreateObject; 说到这你可能还是不很清楚,那么让我们看看MFC的代码吧! CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口 RUNTIME_CLASS(CMyView)); 我们在MFC技术内幕系列之(二)----《MFC文档视图结构内幕 》中曾经说过,所以这里不详述, CSingleDocTemplate::OpenDocumentFile调用了CreateNewFrame CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)//部分源代码 { ...// CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); ...// } m_pFrameClass是CRuntimeClass*指针它指向CMainFrame::classCMainFrame;CreateNewFrame函数调用了 (CFrameWnd*)m_pFrameClass->CreateObject();而CObject* CRuntimeClass::CreateObject()代码如下: //in objcore.cpp CObject* CRuntimeClass::CreateObject() { if (m_pfnCreateObject == NULL) { TRACE(traceAppMsg, 0, _T("Error: Trying to create object which is not ") _T("DECLARE_DYNCREATE /nor DECLARE_SERIAL: %hs./n"), m_lpszClassName); return NULL; }
CObject* pObject = NULL; TRY { pObject = (*m_pfnCreateObject)(); } END_TRY
return pObject; } 它调用了 (*m_pfnCreateObject)();即 CMainFrame::CreateObject();并返回了动态创建的 CMainFrame对象的指针。看到着你应该明白了吧。