• <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>

            【轉(zhuǎn)載之MFC】MFC程序框架分析

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

            一、程序的初始化

            二、消息映射機(jī)制 

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

            流程如下

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

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

            3CTestApp::CTestApp() //調(diào)用自己的構(gòu)造函數(shù)

            4、進(jìn)入WinMain()函數(shù),位于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、進(jìn)入AfxWinMain()函數(shù)中,這是MFC框 架函數(shù),位于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多態(tài)性原理,實際上調(diào)用的是//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中,由此進(jìn)入消息循環(huán)

            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進(jìn)入注冊和創(chuàng)建窗口

            //位于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--

            從③進(jìn)入,再經(jīng)CMainFrame::LoadFrame()進(jìn) 入注冊和創(chuàng)建窗口

             (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)創(chuàng)建窗口CMainFrame::Create(), 之后再調(diào)用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()在調(diào)用CreateWindowEx()創(chuàng) 建真正的窗口對象之前,設(shè)置一個線程級CBT Hook,該hook在 窗口創(chuàng)建完成后被調(diào)用,MFChook函 數(shù)中調(diào)用SetWindowLong()將 該窗口的窗口函數(shù)置換成AfxWndProc

            6、最后再經(jīng)5--nReturnCode = pThread->Run()進(jìn)入消息循環(huán)

            ======================================================================

            消息循環(huán)機(jī)制分析

            nReturnCode = pThread->Run()進(jìn)入。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))

                   {  //發(fā) 送消息到窗口,交由窗口過程函數(shù)處理

                          ::TranslateMessage(&m_msgCur);

            ::DispatchMessage(&m_msgCur);

                   }

                   return TRUE;

            }

             

            所有的窗口都只有一個相同的窗口過程窗口函數(shù)AfxWndProc,消 息傳遞圖如下所示:

            AfxCallWndProc

            WindowProc

            OnWndMsg

            DefWindowProc

            OnWndMsg調(diào)用不成功,調(diào)用默認(rèn)窗口過程函數(shù)

            OnCommand

            命令消息

            OnNotify

            通告消息

            OnCmdMsg

            BOOL CFrameWnd:: OnCmdMsg()

            {

                   CView pView = GetActiveView();//得 到活動視指針

                   if(pView-> OnCmdMsg())

                   return TRUE; //如果CView類 對象或其派生類對象已經(jīng)處理該消息,則返回。

                ……//否則,同 理向下執(zhí)行,交給文檔、框架、及應(yīng)用程序執(zhí)行自身的OnCmdMsg

            }//定義了消息在各個類中的傳遞順序

             

            AfxWndProc

            消息分發(fā)中心

            CWinThread::Run

            PumpMessage

            TranslateMessage

            DispatchMessage

            進(jìn)入消息循環(huán)

            投遞消息到相應(yīng)窗口

            LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)

            {     ……

            CWnd*pWnd=CWnd::FromHandlePermanent(hWnd); //把對句柄的操作轉(zhuǎn)換成對CWnd對 象。

            ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);

            }

             

             

            有關(guān)消息的幾個宏

            DECLARE_MESSAGE_MAP()

            BEGIN_MESSAGE_MAP(theClass, baseClass)END_MESSAGE_MAP()

            弄懂MFC消息映射機(jī)制的最好辦法是將找出一個具體的實例,將這些宏展開,并找

            出相關(guān)的數(shù)據(jù)結(jié)構(gòu)。

            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()作下面三件 事:

            定義一個長度不定的靜態(tài)數(shù)組變量_messageEntries[]

            定義一個靜態(tài)變量messageMap

            定義一個虛擬函數(shù)GetMessageMap()

            DECLARE_MESSAGE_MAP()宏中,涉及到MFC中 兩個對外不公開的數(shù)據(jù)結(jié)構(gòu)

            AFX_MSGMAP_ENTRYAFX_MSGMAP。 為了弄清楚消息映射,有必要考察一下這兩個數(shù)據(jù)結(jié)構(gòu)的定義。

            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)

            };

            結(jié)構(gòu)中各項的含義注釋已經(jīng)說明得很清楚了,這里不再多述,從上面的定義你是否看出,AFX_MSGMAP_ENTRY結(jié) 構(gòu)實際上定義了消息和處理此消息的動作之間的映射關(guān)系。因此靜態(tài)數(shù)組變量_messageEntries[]實 際上定義了一張表,表中的每一項指定了相應(yīng)的對象所要處理的消息和處理此消息的函數(shù)的對應(yīng)關(guān)系,因而這張表也稱為消息映射表。再看看AFX_MSGMAP的 定義。

            2AFX_MSGMAP的 定義

            struct AFX_MSGMAP

            {

            const AFX_MSGMAP* pBaseMap;

            const AFX_MSGMAP_ENTRY* lpEntries;

            };

            不難看出,AFX_MSGMAP定義了單向鏈表, 鏈表中每一項的值是一指向消息映射表的指針(實際上就是_messageEntries的 值)。通過這個鏈表,使得在某個類中調(diào)用基類的的消息處理函數(shù)很容易,因此,“父類的消息處理函數(shù)是子類的缺省消息處理函數(shù)” 就“順理成章”了。在后面的“MFC窗口的消息處理”一節(jié)中會對此作詳細(xì)的講解。

            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 } \

            }; \

            對應(yīng)BEGIN_MESSAGE_MAP()的定義可能不是一下子就看得明白, 不過不要緊,舉

            例子就很清楚了。對于BEGIN_MESSAGE_MAP(CView, CWnd)VC預(yù)編譯器將其展開成下面 的形式:

            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()則不過定義了一個表示映射表 結(jié)束的標(biāo)志項,我想大家對于這種簡單的技巧應(yīng)該是很熟悉的,無需多述。

            到此為止,我想大家也已經(jīng)想到了ON_COMMAND這 樣的宏的具體作用了,不錯它們只不過定義了一種類型的消息映射項,看看ON_COMMAND的定 義:

            #define ON_COMMAND(id, memberFxn) \

            { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn },

            根據(jù)上面的定義,ON_COMMAND(ID_FILE_NEW, OnFileNew)將 被VC預(yù)編譯器展開

            如下:

            {WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv,

            (AFX_PMSG)&OnFileNew},

            到此,MFC的消息映射機(jī)制已經(jīng)清楚了,現(xiàn)在提出并解答兩個問題以作為對這一節(jié) 的小結(jié)。

            為什么不直接使用虛擬函數(shù)實現(xiàn)消息處理函數(shù)呢?這是一個GOOD QUESTION。 前面已經(jīng)說過,MFC的設(shè)計者們在設(shè)計MFC時 有一個很明確的目標(biāo),就是使得“MFC的代碼盡可能小,速度盡可能快”,如果采用虛擬函數(shù),那么對 于所有的窗口消息,都必須有一個與之對應(yīng)的虛擬函數(shù),因而對每一個從CWnd派 生的類而言,都會有一張很大的虛擬函數(shù)表vtbl。但是在實際應(yīng)用中, 一般只對少數(shù)的消息進(jìn)行處理,大部分都交給系統(tǒng)缺省處理,所以表中的大部分項都是無用項,這樣做就浪費了很多內(nèi)存資源,這同MFC設(shè) 計者們的設(shè)計目標(biāo)是相違背的。當(dāng)然,MFC所使用的方法只是解決這類問題的方式之一,不排除還有其 他的解決方式,但就我個人觀點而言,這是一種最好的解決方式,體現(xiàn)了很高的技巧性,值得我們學(xué)習(xí)。

            至于這第二個問題,是由上面的問題引申出來的。如果在子類和父類中出現(xiàn)了相同的消息出來函數(shù),VC編 譯器會怎么處理這個問題呢?VC不會將它們看作錯誤,而會對待虛擬函 數(shù)類似的方式去處理,但對于消息處理函數(shù)(afx_msg前 綴),則不會生成虛擬函數(shù)表vtbl

            ======================================================================

            RTTI實現(xiàn)機(jī)制

            C++設(shè)計者在C++使用的早期并沒有意識到RTTI(運 行時類型檢查)的重要性,后來

            隨作框架結(jié)構(gòu)的類庫出現(xiàn)及其應(yīng)用越來越廣泛,RTTI就 變得越來越重要了。例如下面的這個語句:

            CWnd *pWnd

            任何人都知道對象pCWnd類 型的指針。但是如果有一個類CView是從CWnd派 生來的,對于下面的語句:

            CWnd* CreateView()

            {

            return new CView;

            }

            對于使用CreateView()的 用戶而然,pWnd = CreateView(), 他如何確定pWnd所指向的對象的真正類型呢?因此,必須有一個能夠在 運行時刻就能夠確定指針對象類型的方法,比如給每一個類型的對象均添加一個IsKindOf()之 類的方法,通過此方法判斷指針對象的類型。

            后來,RTTI被加入了C++的 規(guī)范,成為C++一個內(nèi)置的特性。

            MFC的設(shè)計者們設(shè)計MFC的 時候,C++規(guī)范中并沒有包含RTTI,但 是他們很早就意識到這個問題,所以他們以一種獨特的方式在MFC中實現(xiàn)RTTI, 采用這種方式實現(xiàn)的RTTI對于某個對象而言并不是必須的,也就是說,MFC的 設(shè)計者們并不將RTTI強(qiáng)加于用戶所設(shè)計的類型上,而是讓用戶根據(jù)自己的需要選擇是否他所設(shè)計的類 型需要RTTI。因而這種方式比C++規(guī)范 中內(nèi)置的RTTI更靈活。

            MFC的設(shè)計者們在MFC中采用下面的方 法來實現(xiàn)RTTI

            設(shè)計一個基類CObject, 在CObject中增加RTTI功 能,任何一個類型,如果需

            要具有RTTI功能,就必須直接或間接派生于CObject

            采用宏實現(xiàn)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定義了整個從其 派生的家族的所有成員所具有的兩個基本的能力:

            運行時的動態(tài)類型檢查(RTTI)能力和序列化能力。在早期的C++版 本中,沒有規(guī)定RTTI,但MFC的作者們早 就未撲先知,以這種構(gòu)架的形式定義并實現(xiàn)RTTI。體現(xiàn)RTTI的 是CObject中的兩個成員函數(shù):

            virtual CRuntimeClass * GetRuntimeClass() const;

            BOOL IsKindOf(const CRuntimeClass *pClass) const;

            其中,前一個函數(shù)用來訪問存儲RTTI信息的一個CRuntimeClass類 型的結(jié)構(gòu),后一個函數(shù)供在運行時刻進(jìn)行類型判斷。我們先來看看CRuntimeClass結(jié) 構(gòu)的定義,看看它究竟保存了哪些類型信息。

            <<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保存類的實例數(shù)據(jù)所占內(nèi)存的大 小。我們重點要關(guān)注的是m_pBaseClass成員,它是指向名稱為m_lpszClassName的 類的基類的CRuntimeClass的指 針,因此,CRuntimeClass就形成了一個繼承鏈表,這個鏈表 記錄了某一族類的繼承關(guān)系。

            RTTI的實現(xiàn):

            實現(xiàn)RTTI的除了上面兩個函數(shù)外,還有幾個相關(guān)的宏。我們先看看GetRuntimeClass()IsKindOf()的 實現(xiàn).

            1GetRuntimeClass()的 實現(xiàn)

            CRuntimeClass* CObject::GetRuntimeClass() const

            {

            return RUNTIME_CLASS(CObject);

            }

            關(guān)鍵就在RUNTIME_CLASS這個宏上,RUNTIME_CLASS宏 的實現(xiàn)如下:

            #define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

            將宏展開,上面的實現(xiàn)就變成:

            CRuntimeClass* CObject::GetRuntimeClass() const

            {

            return (CRuntimeClass*)(&CObject::classCObject);

            }

            也就是說,它返回CObject類的一個static型 的成員classCObject

            2IsKindOf()的 實現(xiàn)

            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);

            }

            前兩行我們不管它,關(guān)鍵在于最后一行pClassThis->IsDerivedFrom(pClass),歸 根結(jié)底就是調(diào)用CRuntimeClassIsDerivedFrom()方 法。下面是CRuntimeClass的成員IsDerivedFrom()的 實現(xiàn):

            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

            }

            關(guān)鍵是上面的一段循環(huán)代碼:

            while (pClassThis != NULL)

            {

            if (pClassThis == pBaseClass) return TRUE;

            pClassThis = pClassThis->m_pBaseClass;

            }

            它從繼承鏈表的某一節(jié)點this開始,向后搜索比較,確定繼承關(guān)系。

            將到這里,或許有人要問,這些CRuntimeClass結(jié) 構(gòu)是如何產(chǎn)生的呢?這是一個很好的問題,解決了這個問題,就完全清楚了MFCRTTI的 實現(xiàn)。使用過Visual C++開發(fā)程序的人都應(yīng)該記得DECLARE_DYNAMICIMPLEMENT_DYNAMIC這 兩個宏,它們分別用來定義相應(yīng)類的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宏用來在類的定義中定義靜態(tài)CRuntimeClass變 量和虛擬GetRuntimeClass()函 數(shù)。可以推斷,IMPLEMENT_DYNAMIC宏一定是用來初始化該靜態(tài)變量和實現(xiàn)GetRuntimeClass()函 數(shù),。不錯,正是這樣!

            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)展開如下:

            //下面展開的代碼用來初始化靜態(tài)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

            }

            //下面的代碼用來實現(xiàn)GetRuntimeClass()函 數(shù)

            CRuntimeClass* CView::GetRuntimeClass() const

            { return (CRuntimeClass*)(&CView::classCView);}

            總的來說,同RTTI有關(guān)的宏有下面幾對:

            DECLARE_DYNAMICIMPLEMENT_DYNAMIC

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

            DECLARE_DYNCREATEIMPLEMENT_DYNCREATE

            一對宏除了能夠提供類型判斷能力外,還能夠提供動態(tài)創(chuàng)建對象的能力。

            (定義并實現(xiàn)IsKindOf()CreateObject()

            DECLARE_SERIALIMPLEMENT_SERIAL

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

            (定義并實現(xiàn)IsKindOf()CreateObject()Serialize()

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


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導(dǎo)航

            統(tǒng)計

            常用鏈接

            留言簿

            隨筆分類

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            精品久久久一二三区| 久久久久成人精品无码 | 久久精品视频一| 一级做a爰片久久毛片人呢| 久久精品国产亚洲av麻豆图片| 国产精品美女久久久久网| 亚洲伊人久久精品影院 | 久久人人爽人人爽人人爽| 久久午夜综合久久| 久久亚洲国产成人影院网站| 国产成人精品久久一区二区三区av| 久久91精品国产91久久小草| 亚洲欧美国产日韩综合久久| 伊人久久精品线影院| 久久国产成人午夜aⅴ影院 | 日本三级久久网| 久久成人国产精品二三区| 亚洲国产精品久久久久| 成人a毛片久久免费播放| 久久精品国产只有精品66| 久久人人爽人爽人人爽av| 国内精品伊人久久久久妇| 亚洲精品无码久久久影院相关影片 | 欧美精品福利视频一区二区三区久久久精品 | 一本一道久久综合狠狠老 | 久久久国产精品| 久久精品中文字幕大胸| 国产亚洲欧美精品久久久| 99久久99久久精品国产| 色婷婷噜噜久久国产精品12p| 久久婷婷人人澡人人爽人人爱| 久久精品一本到99热免费| 狠狠精品久久久无码中文字幕 | 香蕉久久一区二区不卡无毒影院| 国产激情久久久久影院小草| 久久婷婷是五月综合色狠狠| 国产精品久久久久久久久鸭 | 93精91精品国产综合久久香蕉| 色播久久人人爽人人爽人人片aV| 日韩AV无码久久一区二区| 久久人人爽人人爽人人片AV高清 |