【转载之MFC】MFC程序框架分析
From: CSDN qzmguy的专栏
主要包括两个 方面
一、程序的初始化
二、消息映射机制
以单文档程序Test为例。程序自动生成的类为CAboutDlg、CMainFrame、CTestApp、CTestDoc、CTestView。 还有一个全局的应用程序类对象CTestApp theApp。
流程如下:
1、CTestApp theApp; //初始化全局对象
2、因为Class CTestApp:public CWinApp,所以进 入CWinApp::CWinApp(LPCTSTR lpszAppName)<位于AppCore.cpp中>
3、CTestApp::CTestApp() //调用自己的构造函数
4、进入WinMain()函数,位于AppMODUL.cpp中
//在TCHAR.h中 这么一行定义: #define _tWinMain WinMain _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } |
5、进入AfxWinMain()函数中,这是MFC框 架函数,位于WinMain.cpp中
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL);
int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp();
// AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure;
// App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) goto InitFailure;
// Perform specific initializations多态性原理,实际上调用的是//CTestApp::InitInstance() ① if (!pThread->InitInstance()) { if (pThread->m_pMainWnd != NULL) { TRACE0("Warning: Destroying non-NULL m_pMainWnd\n"); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } //CWinThread::Run()位 于THRDCORE.cpp中,由此进入消息循环 ② nReturnCode = pThread->Run();
InitFailure: #ifdef _DEBUG // Check for missing AfxLockTempMap calls if (AfxGetModuleThreadState()->m_nTempMapLock != 0) { TRACE1("Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock); } AfxLockTempMaps(); AfxUnlockTempMaps(-1); #endif
AfxWinTerm(); return nReturnCode; } |
5-① |
BOOL CTestApp::InitInstance() { AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif // Change the registry key under which our settings are stored. // TODO: You should modify this string to be something appropriate // such as the name of your company or organization. SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(); // Load standard INI file options (including MRU) // Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views. CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTestDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CTestView)); AddDocTemplate(pDocTemplate); // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line进入注册和创建窗口 //位于AppUI2.cpp中 ③ if (!ProcessShellCommand(cmdInfo)) return FALSE; // The one and only window has been initialized, so show and update it. //显 示和更新窗口 m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } |
5-①-③ |
从③进入,再经CMainFrame::LoadFrame()进 入注册和创建窗口 (1)注 册窗口:AfxEndDeferRegisterClass() BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister) { // mask off all classes that are already registered AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); fToRegister &= ~pModuleState->m_fRegisteredClasses; if (fToRegister == 0) return TRUE;
LONG fRegisteredClasses = 0;
// common initialization WNDCLASS wndcls; memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults wndcls.lpfnWndProc = DefWindowProc; wndcls.hInstance = AfxGetInstanceHandle(); wndcls.hCursor = afxData.hcurArrow;
INITCOMMONCONTROLSEX init; init.dwSize = sizeof(init);
// work to register classes as specified by fToRegister, populate fRegisteredClasses as we go …………………………………………………… if (fToRegister & AFX_WNDFRAMEORVIEW_REG) { // SDI Frame or MDI Child windows or views - normal colors wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)) fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG; } ………………………………………………………… return (fToRegister & fRegisteredClasses) == fToRegister; } |
AFX_STATIC BOOL AFXAPI _AfxRegisterWithIcon(WNDCLASS* pWndCls, LPCTSTR lpszClassName, UINT nIDIcon) { pWndCls->lpszClassName = lpszClassName; HINSTANCE hInst = AfxFindResourceHandle( MAKEINTRESOURCE(nIDIcon), RT_GROUP_ICON); if ((pWndCls->hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(nIDIcon))) == NULL) { // use default icon pWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION); } return AfxRegisterClass(pWndCls); } |
(2)创建窗口CMainFrame::Create(), 之后再调用CreateEx() BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam) { ……………………………………………………………… AfxHookWindowCreate(this); HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if CreateWindowEx fails too soon if (hWnd == NULL) return FALSE; ASSERT(hWnd == m_hWnd); // should have been set in send msg hook return TRUE; } 从上面的代码看不出任何显式的置换DefWindowProc的 代码,其实,它隐藏在AfxHookWindowCreate(this)之 中,顺藤摸瓜,再看看AfxHookWindowCreate()的 代码。CreateEx()在调用CreateWindowEx()创 建真正的窗口对象之前,设置一个线程级的CBT Hook,该hook在 窗口创建完成后被调用,MFC在hook函 数中调用SetWindowLong()将 该窗口的窗口函数置换成AfxWndProc。 |
6、最后再经5-①-②nReturnCode = pThread->Run()进入消息循环
======================================================================
消息循环机制分析
从nReturnCode = pThread->Run()进入。CWinThread::Run()位 于THRDCORE.cpp中
int CWinThread::Run() { ASSERT_VALID(this); // for tracking the idle time state BOOL bIdle = TRUE; LONG lIdleCount = 0; // acquire and dispatch messages until a WM_QUIT message is received. for (;;) { // phase1: check to see if we can do idle work while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)) { // call OnIdle while in bIdle state if (!OnIdle(lIdleCount++)) bIdle = FALSE; // assume "no idle" state } // phase2: pump messages while available do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); } ASSERT(FALSE); // not reachable } |
BOOL CWinThread::PumpMessage() { ASSERT_VALID(this); if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) { #ifdef _DEBUG if (afxTraceFlags & traceAppMsg) TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n"); m_nDisablePumpCount++; // application must die // Note: prevents calling message loop things in 'ExitInstance' // will never be decremented #endif return FALSE; } #ifdef _DEBUG if (m_nDisablePumpCount != 0) { TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n"); ASSERT(FALSE); } #endif #ifdef _DEBUG if (afxTraceFlags & traceAppMsg) _AfxTraceMsg(_T("PumpMessage"), &m_msgCur); #endif // process this message if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { //发 送消息到窗口,交由窗口过程函数处理 ::TranslateMessage(&m_msgCur); ::DispatchMessage(&m_msgCur); } return TRUE; } |
所有的窗口都只有一个相同的窗口过程窗口函数AfxWndProc,消 息传递图如下所示:
AfxCallWndProc |
WindowProc |
OnWndMsg |
DefWindowProc |
OnWndMsg调用不成功,调用默认窗口过程函数 |
OnCommand |
命令消息 |
OnNotify |
通告消息 |
OnCmdMsg |
BOOL CFrameWnd:: OnCmdMsg(…) { CView pView = GetActiveView();//得 到活动视指针。 if(pView-> OnCmdMsg(…)) return TRUE; //如果CView类 对象或其派生类对象已经处理该消息,则返回。 ……//否则,同 理向下执行,交给文档、框架、及应用程序执行自身的OnCmdMsg。 }//定义了消息在各个类中的传递顺序
|
AfxWndProc |
消息分发中心 |
CWinThread::Run |
PumpMessage |
TranslateMessage DispatchMessage |
进入消息循环 |
投递消息到相应窗口 |
LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam) { …… CWnd*pWnd=CWnd::FromHandlePermanent(hWnd); //把对句柄的操作转换成对CWnd对 象。 ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam); } |
有关消息的几个宏
DECLARE_MESSAGE_MAP()宏
BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏
弄懂MFC消息映射机制的最好办法是将找出一个具体的实例,将这些宏展开,并找
出相关的数据结构。
DECLARE_MESSAGE_MAP()
DECLARE_MESSAGE_MAP()宏的定义如下:
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
从上面的定义可以看出,DECLARE_MESSAGE_MAP()作下面三件 事:
定义一个长度不定的静态数组变量_messageEntries[];
定义一个静态变量messageMap;
定义一个虚拟函数GetMessageMap();
在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中 两个对外不公开的数据结构
AFX_MSGMAP_ENTRY和AFX_MSGMAP。 为了弄清楚消息映射,有必要考察一下这两个数据结构的定义。
AFX_MSGMAP_ENTRY的定义
struct AFX_MSGMAP_ENTRY
{
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_MSGMAP_ENTRY结 构实际上定义了消息和处理此消息的动作之间的映射关系。因此静态数组变量_messageEntries[]实 际上定义了一张表,表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系,因而这张表也称为消息映射表。再看看AFX_MSGMAP的 定义。
(2)AFX_MSGMAP的 定义
struct AFX_MSGMAP
{
const AFX_MSGMAP* pBaseMap;
const AFX_MSGMAP_ENTRY* lpEntries;
};
不难看出,AFX_MSGMAP定义了一单向链表, 链表中每一项的值是一指向消息映射表的指针(实际上就是_messageEntries的 值)。通过这个链表,使得在某个类中调用基类的的消息处理函数很容易,因此,“父类的消息处理函数是子类的缺省消息处理函数” 就“顺理成章”了。在后面的“MFC窗口的消息处理”一节中会对此作详细的讲解。
BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()
它们的定义如下:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{ \
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
对应BEGIN_MESSAGE_MAP()的定义可能不是一下子就看得明白, 不过不要紧,举
一例子就很清楚了。对于BEGIN_MESSAGE_MAP(CView, CWnd),VC预编译器将其展开成下面 的形式:
const AFX_MSGMAP* CView::GetMessageMap() const
{
return &CView::messageMap;
}
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap =
{
&CWnd::messageMap,
&CView::_messageEntries[0]
};
AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries[] =
{
至于END_MESSAGE_MAP()则不过定义了一个表示映射表 结束的标志项,我想大家对于这种简单的技巧应该是很熟悉的,无需多述。
到此为止,我想大家也已经想到了象ON_COMMAND这 样的宏的具体作用了,不错它们只不过定义了一种类型的消息映射项,看看ON_COMMAND的定 义:
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn },
根据上面的定义,ON_COMMAND(ID_FILE_NEW, OnFileNew)将 被VC预编译器展开
如下:
{WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv,
(AFX_PMSG)&OnFileNew},
到此,MFC的消息映射机制已经清楚了,现在提出并解答两个问题以作为对这一节 的小结。
为什么不直接使用虚拟函数实现消息处理函数呢?这是一个GOOD QUESTION。 前面已经说过,MFC的设计者们在设计MFC时 有一个很明确的目标,就是使得“MFC的代码尽可能小,速度尽可能快”,如果采用虚拟函数,那么对 于所有的窗口消息,都必须有一个与之对应的虚拟函数,因而对每一个从CWnd派 生的类而言,都会有一张很大的虚拟函数表vtbl。但是在实际应用中, 一般只对少数的消息进行处理,大部分都交给系统缺省处理,所以表中的大部分项都是无用项,这样做就浪费了很多内存资源,这同MFC设 计者们的设计目标是相违背的。当然,MFC所使用的方法只是解决这类问题的方式之一,不排除还有其 他的解决方式,但就我个人观点而言,这是一种最好的解决方式,体现了很高的技巧性,值得我们学习。
至于这第二个问题,是由上面的问题引申出来的。如果在子类和父类中出现了相同的消息出来函数,VC编 译器会怎么处理这个问题呢?VC不会将它们看作错误,而会象对待虚拟函 数类似的方式去处理,但对于消息处理函数(带afx_msg前 缀),则不会生成虚拟函数表vtbl。
======================================================================
RTTI实现机制
C++设计者在C++使用的早期并没有意识到RTTI(运 行时类型检查)的重要性,后来
随作框架结构的类库出现及其应用越来越广泛,RTTI就 变得越来越重要了。例如下面的这个语句:
CWnd *pWnd;
任何人都知道对象p是CWnd类 型的指针。但是如果有一个类CView是从CWnd派 生来的,对于下面的语句:
CWnd* CreateView()
{
return new CView;
}
对于使用CreateView()的 用户而然,pWnd = CreateView(), 他如何确定pWnd所指向的对象的真正类型呢?因此,必须有一个能够在 运行时刻就能够确定指针对象类型的方法,比如给每一个类型的对象均添加一个IsKindOf()之 类的方法,通过此方法判断指针对象的类型。
后来,RTTI被加入了C++的 规范,成为C++一个内置的特性。
在MFC的设计者们设计MFC的 时候,C++规范中并没有包含RTTI,但 是他们很早就意识到这个问题,所以他们以一种独特的方式在MFC中实现RTTI, 采用这种方式实现的RTTI对于某个对象而言并不是必须的,也就是说,MFC的 设计者们并不将RTTI强加于用户所设计的类型上,而是让用户根据自己的需要选择是否他所设计的类 型需要RTTI。因而这种方式比C++规范 中内置的RTTI更灵活。
MFC的设计者们在MFC中采用下面的的方 法来实现RTTI:
设计一个基类CObject, 在CObject中增加RTTI功 能,任何一个类型,如果需
要具有RTTI功能,就必须直接或间接派生于CObject
采用宏实现RTTI,对于某个直接或间接从CObject派 生来的类型而言,该宏可
有可无,如果有该宏,它就具有RTTI功能,反之则无。
<一>考察CObject
我们先从CObject开始,下面是它的定义:
class AFX_NOVTABLE CObject
{
public:
// Object model (types, destruction, allocation)
virtual CRuntimeClass* GetRuntimeClass() const;
virtual ~CObject(); // virtual destructors are necessary
// Diagnostic allocations
void* PASCAL operator new(size_t nSize);
void* PASCAL operator new(size_t, void* p);
void PASCAL operator delete(void* p);
void PASCAL operator delete(void* p, void* pPlace);
void PASCAL operator delete(void *p, LPCSTR lpszFileName, int nLine);
// Disable the copy constructor and assignment by default so you will get
// compiler errors instead of unexpected behaviour if you pass objects
// by value or assign objects.
protected:
CObject();
private:
CObject(const CObject& objectSrc); // no implementation
void operator=(const CObject& objectSrc); // no implementation
// Attributes
public:
BOOL IsSerializable() const;
BOOL IsKindOf(const CRuntimeClass* pClass) const;
// Overridables
virtual void Serialize(CArchive& ar);
// Implementation
public:
static const AFX_DATA CRuntimeClass classCObject;
};
总的来说,CObject定义了整个从其 派生的家族的所有成员所具有的两个基本的能力:
运行时的动态类型检查(RTTI)能力和序列化能力。在早期的C++版 本中,没有规定RTTI,但MFC的作者们早 就未扑先知,以这种构架的形式定义并实现RTTI。体现RTTI的 是CObject中的两个成员函数:
virtual CRuntimeClass * GetRuntimeClass() const;
BOOL IsKindOf(const CRuntimeClass *pClass) const;
其中,前一个函数用来访问存储RTTI信息的一个CRuntimeClass类 型的结构,后一个函数供在运行时刻进行类型判断。我们先来看看CRuntimeClass结 构的定义,看看它究竟保存了哪些类型信息。
<<From afx.h>>
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;
// Operations
CObject* CreateObject();
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// 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
};
上面就是CRuntimeClass的定义,m_lpszClassName保 存类的名称,m_nObjectSize保存类的实例数据所占内存的的大 小。我们重点要关注的是m_pBaseClass成员,它是指向名称为m_lpszClassName的 类的基类的CRuntimeClass的指 针,因此,CRuntimeClass就形成了一个继承链表,这个链表 记录了某一族类的继承关系。
RTTI的实现:
实现RTTI的除了上面两个函数外,还有几个相关的宏。我们先看看GetRuntimeClass()和IsKindOf()的 实现.
1.GetRuntimeClass()的 实现
CRuntimeClass* CObject::GetRuntimeClass() const
{
return RUNTIME_CLASS(CObject);
}
关键就在RUNTIME_CLASS这个宏上,RUNTIME_CLASS宏 的实现如下:
#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
将宏展开,上面的实现就变成:
CRuntimeClass* CObject::GetRuntimeClass() const
{
return (CRuntimeClass*)(&CObject::classCObject);
}
也就是说,它返回CObject类的一个static型 的成员classCObject。
2.IsKindOf()的 实现
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);
}
前两行我们不管它,关键在于最后一行pClassThis->IsDerivedFrom(pClass),归 根结底就是调用CRuntimeClass的IsDerivedFrom()方 法。下面是CRuntimeClass的成员IsDerivedFrom()的 实现:
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;
while (pClassThis != NULL)
{
if (pClassThis == pBaseClass) return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
return FALSE; // walked to the top, no match
}
关键是上面的一段循环代码:
while (pClassThis != NULL)
{
if (pClassThis == pBaseClass) return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
它从继承链表的某一节点this开始,向后搜索比较,确定继承关系。
将到这里,或许有人要问,这些CRuntimeClass结 构是如何产生的呢?这是一个很好的问题,解决了这个问题,就完全清楚了MFC中RTTI的 实现。使用过Visual C++开发程序的人都应该记得DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC这 两个宏,它们分别用来定义相应类的static CRuntimeClass成 员和对该成员初始化。
DECLARE_DYNAMIC宏的定义:
#define DECLARE_DYNAMIC(class_name) \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
例如DECLARE_DYNAMIC(CView)展 开成为:
public:
static const AFX_DATA CRuntimeClass classCView;
virtual CRuntimeClass* GetRuntimeClass() const;
由此可见,DECLARE_DYNAMIC宏用来在类的定义中定义静态CRuntimeClass变 量和虚拟GetRuntimeClass()函 数。可以推断,IMPLEMENT_DYNAMIC宏一定是用来初始化该静态变量和实现GetRuntimeClass()函 数,。不错,正是这样!
IMPLEMENT_DYNAMIC宏的定义:
#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) \
AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); } \
例如IMPLEMENT_DYNAMIC(CView, CWnd)展开如下:
//下面展开的代码用来初始化静态CRuntimeClass变 量
AFX_COMDATA const AFX_DATADEF CRuntimeClass CView::classCView =
{
“CView”, //m_lpszClassName
sizeof(class CView), //m_nObjectSize
0xffff, //m_wSchema
NULL, //m_pfnCreateObject
(CRuntimeClass*)(&CWnd::classCWnd), //m_pBaseClass
NULL //m_pNextClass
}
//下面的代码用来实现GetRuntimeClass()函 数
CRuntimeClass* CView::GetRuntimeClass() const
{ return (CRuntimeClass*)(&CView::classCView);}
总的来说,同RTTI有关的宏有下面几对:
DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC
这一对宏能够提供运行是类型判断能力。(定义并实现IsKindOf())
DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE
这一对宏除了能够提供类型判断能力外,还能够提供动态创建对象的能力。
(定义并实现IsKindOf()和CreateObject())
DECLARE_SERIAL和IMPLEMENT_SERIAL
这一对宏除了提供类型判断能力、动态创建对象能力外,还具有序列化功能。
(定义并实现IsKindOf()、CreateObject()和Serialize())