青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

【轉載之MFC】MFC程序框架分析

From: CSDN  qzmguy的專欄
主要包括兩個 方面

一、程序的初始化

二、消息映射機制 

以單文檔程序Test為例。程序自動生成的類為CAboutDlgCMainFrameCTestAppCTestDocCTestView。 還有一個全局的應用程序類對象CTestApp theApp

流程如下

1CTestApp theApp; //初始化全局對象

2、因為Class CTestApp:public CWinApp,所以進 入CWinApp::CWinApp(LPCTSTR lpszAppName)<位于AppCore.cpp>

3CTestApp::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在 窗口創建完成后被調用,MFChook函 數中調用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_ENTRYAFX_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的 定義。

2AFX_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

任何人都知道對象pCWnd類 型的指針。但是如果有一個類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()的 實現.

1GetRuntimeClass()的 實現

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

2IsKindOf()的 實現

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),歸 根結底就是調用CRuntimeClassIsDerivedFrom()方 法。下面是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結 構是如何產生的呢?這是一個很好的問題,解決了這個問題,就完全清楚了MFCRTTI的 實現。使用過Visual C++開發程序的人都應該記得DECLARE_DYNAMICIMPLEMENT_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_DYNAMICCView, 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_DYNAMICIMPLEMENT_DYNAMIC

一對宏能夠提供運行是類型判斷能力。(定義并實現IsKindOf()

DECLARE_DYNCREATEIMPLEMENT_DYNCREATE

一對宏除了能夠提供類型判斷能力外,還能夠提供動態創建對象的能力。

(定義并實現IsKindOf()CreateObject()

DECLARE_SERIALIMPLEMENT_SERIAL

一對宏除了提供類型判斷能力、動態創建對象能力外,還具有序列化功能。

(定義并實現IsKindOf()CreateObject()Serialize()

posted on 2010-03-26 21:35 LynnRaymond 閱讀(653) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

導航

統計

常用鏈接

留言簿

隨筆分類

隨筆檔案

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            99视频在线观看一区三区| 久久欧美肥婆一二区| 国产精品午夜在线| 欧美日韩xxxxx| 欧美日韩成人一区| 欧美日韩亚洲一区二区三区| 欧美日韩国语| 国产精品乱看| 国产啪精品视频| 在线观看欧美日韩| 亚洲精品久久久久久下一站| 99视频一区二区| 香蕉av福利精品导航| 久久久xxx| 亚洲三级色网| 日韩亚洲欧美在线观看| 午夜电影亚洲| 能在线观看的日韩av| 欧美日韩一二三四五区| 国产一区二区三区在线播放免费观看| 亚洲国产日韩欧美一区二区三区| 亚洲婷婷国产精品电影人久久| 久久精品官网| 一本大道久久a久久综合婷婷| 久久国产精品99国产精| 欧美日韩一区不卡| 亚洲福利电影| 久久国产精品久久精品国产| 最新日韩av| 久久久久www| 国产精品久久久久久久久搜平片| 亚洲高清自拍| 欧美性久久久| 黄色成人在线观看| 亚洲一区二区三区色| 欧美大片18| 香蕉久久国产| 国产精品久久久99| 99成人精品| 麻豆freexxxx性91精品| 亚洲四色影视在线观看| 欧美激情视频在线播放| 韩日成人在线| 久久精品一区四区| 亚洲天堂视频在线观看| 欧美精品一区二区三| 亚洲国产成人在线播放| 久久久久久穴| 午夜精品久久久久| 国产精品vip| 一本久久综合亚洲鲁鲁| 欧美国产免费| 久久成人亚洲| 国产一区自拍视频| 久久久久成人精品| 久久aⅴ国产紧身牛仔裤| 国产精品视频免费在线观看| 亚洲视频免费观看| 99国产精品99久久久久久粉嫩| 老司机成人在线视频| 国内精品久久久久影院薰衣草| 午夜精品婷婷| 一本色道久久综合亚洲二区三区| 欧美经典一区二区三区| 91久久精品网| 欧美激情视频在线播放 | 香蕉国产精品偷在线观看不卡| 亚洲肉体裸体xxxx137| 欧美精品在线观看一区二区| 99热在这里有精品免费| 亚洲日本理论电影| 欧美精品日日鲁夜夜添| 亚洲最新合集| 一区二区三区成人| 国产精品久久亚洲7777| 久久久www成人免费无遮挡大片 | 欧美日韩岛国| 亚洲一区二区欧美| 亚洲欧美日本国产有色| 黑人一区二区| 亚洲国产成人在线播放| 欧美日韩在线播放一区| 欧美一区二区视频在线观看| 久久成人免费日本黄色| 亚洲国产婷婷香蕉久久久久久| 日韩图片一区| 日韩视频精品在线观看| 国产麻豆日韩| 欧美成人精品在线观看| 欧美日韩免费观看一区三区 | 欧美日韩亚洲综合| 欧美在线国产| 老司机亚洲精品| 一本色道88久久加勒比精品 | 欧美国产日韩xxxxx| 免费一级欧美片在线观看| 一区二区三区免费观看| 久久国产精品电影| 一区二区三区产品免费精品久久75| 亚洲欧美日韩国产成人| 最新国产の精品合集bt伙计| 亚洲无限av看| 亚洲日韩成人| 欧美一区二区三区在线视频| 亚洲日本va午夜在线电影| 亚洲免费小视频| 亚洲免费电影在线| 久久久亚洲综合| 欧美在线一区二区| 欧美视频在线观看免费| 欧美黄色影院| 狠狠色狠狠色综合日日五| 亚洲一区观看| 亚洲免费视频观看| 欧美屁股在线| 亚洲国内精品| 影音先锋亚洲精品| 欧美一级欧美一级在线播放| 亚洲视频专区在线| 欧美激情一区二区三区在线视频观看| 久久成人精品无人区| 欧美系列精品| av成人天堂| 一区二区三区色| 欧美激情中文字幕一区二区| 免费在线欧美视频| 黄色一区二区在线| 欧美亚洲综合另类| 欧美一区二区三区男人的天堂| 欧美精品啪啪| 亚洲国产欧美一区| 亚洲精品欧美激情| 欧美成人黑人xx视频免费观看| 蜜桃av噜噜一区| 国产一区清纯| 久久er99精品| 麻豆av一区二区三区久久| 有码中文亚洲精品| 蜜臀久久99精品久久久画质超高清| 欧美jizz19hd性欧美| 最新国产成人av网站网址麻豆| 欧美黄色影院| 一区二区高清视频在线观看| 亚洲一区二区在线观看视频| 国产精品视频内| 久久国产福利| 亚洲激情欧美| 亚洲欧美日本伦理| 国产在线拍偷自揄拍精品| 久久久久久网| 日韩一二三在线视频播| 欧美剧在线观看| 亚洲视频在线观看网站| 久久福利毛片| 亚洲国产天堂久久综合| 欧美日韩亚洲高清一区二区| 亚洲桃花岛网站| 久久久久99| 日韩午夜激情av| 国产精品乱码久久久久久| 午夜精彩视频在线观看不卡| 久久五月天婷婷| 99精品免费视频| 国产伦精品一区二区三区高清版| 欧美一级片久久久久久久| 欧美第一黄色网| 亚洲手机视频| 尤妮丝一区二区裸体视频| 欧美搞黄网站| 亚洲综合99| 亚洲风情亚aⅴ在线发布| 亚洲嫩草精品久久| 国语自产精品视频在线看| 欧美成人在线影院| 亚洲理论在线观看| 久久久一本精品99久久精品66| 亚洲精品视频在线观看免费| 国产人妖伪娘一区91| 欧美精品在线播放| 久久精品成人欧美大片古装| 99国产精品视频免费观看| 久久综合中文色婷婷| 亚洲女与黑人做爰| 亚洲精品社区| 亚洲国产精品成人久久综合一区| 欧美性猛交一区二区三区精品| 久久av老司机精品网站导航| 日韩一区二区福利| 欧美激情一区二区三区全黄| 久久国产精品久久久久久久久久| 亚洲美女中文字幕| 亚洲第一页自拍| 国产一区二区三区自拍| 欧美性做爰猛烈叫床潮| 欧美激情视频一区二区三区在线播放| 久久久国产成人精品| 性18欧美另类| 亚洲一区综合| 日韩一级视频免费观看在线| 亚洲国产乱码最新视频|