MFC六大關鍵技術


MFC六大關鍵技術包括:

  • MFC Initialization —— MFC程序的初始化過程
  • RTTI(Runtime Type Information)—— 運行時類型識別
  • Dynamic Creation —— 動態創建
  • Persistence ——永久保存(串行化、序列化)
  • Message Mapping —— 消息映射
  • Message Routing —— 消息傳遞

MFC程序的初始化過程

首先,我們用VS2010建立一個Win32應用程序,在項目的配置屬性中鏈接MFC庫,並輸入以下代碼:

#include <afxwin.h>
class MyApp : public CWinApp
{
public:
    BOOL InitInstance() //②程序入點
    {
        CFrameWnd *Frame=new CFrameWnd();//構造框架
        m_pMainWnd=Frame; //將m_pMainWnd 設定為Frame;
        Frame->Create(NULL,_T("最簡單的窗口"));//建立框架
        Frame->ShowWindow(SW_SHOW); //顯示框架
        return true; //返回
    }
};
MyApp theApp; //①建立應用程序。

運行結果:

然后再更換為以下代碼:

#include <afxwin.h>
class MyApp : public CWinApp
{
public:
    BOOL InitInstance() //②程序入點
    {
        AfxMessageBox(_T("程序依然可以運行!"));
        return true; //返回
    }
};
MyApp theApp; //①建立應用程序。

程序運行結果為:

  我們知道,C++控制台程序的入口點函數為main()函數,而Windows應用程序的入口點函數為WinMain()。然而,上述程序並沒有main()或WinMain()函數,也能運行。實際上,在main()或WinMain()函數執行之前,全局對象會先運行。在上述程序中我們定義了全局對象theApp,程序會首先執行theApp。只要我們構造了CWinApp 對象,就可以執行WinMain()函數。

  整個過程大體為:theApp對象初始化-->調用類名構造函數-->調用AfxWinMain函數-->調用CWinApp類的成員函數完成各種初始化(包括InitApplication 、InitInstance 、Run(包含消息循環))-->Winmain函數執行。

  例如,我們再建立一個Win32控制台程序,代碼如下:

#include<iostream>
using namespace std;

class test
{
public:
    test()
    {
        cout<<"請改變你對main()函數的看法!"<<endl;
    }
};
test test1;

int main()
{
    system("pause");
    return 0;
}

運行結果:

程序首先執行了全局對象test1的構造函數。

  在MFC中,InitApplication()和InitInstance()為CWinApp的兩個虛函數,前者負責”每個程序只做一次“的操作,后者負責”每個例程都得做一次“的操作。在Windows應用程序中,如果我們想改變窗口的屬性,只需改寫初始化函數InitInstance()即可。

運行時類型識別

  這方面請閱讀我的另一篇博客:RTTI(運行時類型識別):http://www.cnblogs.com/gaohongchen01/p/4085908.html

動態創建

  MFC中很多地方都使用了動態創建技術,動態創建就是在程序運行時創建指定類的對象。例如MFC的單文檔程序中,文檔模板類的對象就動態創建了框架窗口對象、文檔對象和視圖對象。動態創建技術對於希望了解MFC底層運行機制的朋友來說,非常有必要弄清楚。

  要做到把自己的類交給MFC,MFC用同一方法把不同的類一一准確創建,我們就要用到鏈表,記錄各類的關鍵信息,在動態創建的時候找出這些信息。

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);
    static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);
    static CObject* PASCAL CreateObject(LPCSTR lpszClassName);
    static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);

// 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;
};

  簡單地說m_pfnCreateObject保存了一個函數的地址,將會創建一個對象,m_pfnCreateObject指向不同的函數,我們就會創建不同類型的對象。CreateObject()即為m_pfnCreateObject指向的函數。這樣,我們用函數指針m_pfnCreateObject,就隨時可new新對象了。

  在設計CRuntimeClass類時,只有類名(和基類名)的不同,這正是我們想要的,因為動態創建也象RTTI那樣用到兩個宏,只要傳入類名和基類作宏參數,就可以滿足條件。類聲明中使用DECLARE_DYNCREATE(CLASSNMAE)宏和在類的實現文件中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏來為我們加入鏈表。

  m_pBaseClass指針只會沿着基類上去,會漏掉其它分支。在動態創建時,必需檢查整個鏈表,看有多少個要動態創建的對象,即是說要從表頭(pFirstClass)開始一直遍歷到表尾(m_pNextClass=NULL),不能漏掉一個CRuntimeClass對象。所以每當有一個新的鏈表元素要加入鏈表時,就要使新的鏈表元素成為表頭,且m_pNextClass指向原來鏈表的表頭,即像下面那樣(當然,這些不需要我們操心,是RTTI宏幫助我們完成的):

pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;//新元素的m_pNextClass指針指向想加入的鏈表的表頭。   
CRuntimeClass::pFirstClass=pNewClass;//鏈表的頭指針指向剛插入的新元素。 

有了上面的鏈表,我們就可以分析動態創建了。

動態創建的步驟

       有了一個包含類名,函數指針,動態創建函數的鏈表,我們就可以知道應該按什么步驟去動態創建了:

  1. 獲得一要動態創建的類的類名(假設為A)
  2. 將A跟鏈表里面每個元素的m_lpszClassName指向的類名作比較
  3. 若找到跟A相同的類名就返回A所屬的CRuntimeClass元素的指針
  4. 判斷m_pfnCreateObject是否有指向創建函數,有則創建對象,並返回該對象

代碼演示如下(以下兩個函數都是CRuntimeClass類函數):

///////////////以下為根據類名從表頭向表尾查找所屬的CRuntimeClass對象////////////   
  
CRuntimeClass* PASCAL CRuntimeClass::Load()   
{   
char szClassXXX[64];   
CRuntimeClass* pClass;   
cin>>szClassXXX;      //假定這是我們希望動態創建的類名   
for(pClass=pFirstClass;pClass!=NULL;pClass=pClass->m_pNextClass)   
{   
     if(strcmp(szClassXXX,pClass->m_lpszClassName)==0)   
     return pClass;   
}   
     return NULL;   
}   
  
///////////根據CRuntimeClass創建對象///////////   
CObject* CRuntimeClass::CreateObject()   
{   
     if(m_pfnCreateObject==NULL) return NULL;   
     CObject *pObject;   
     pObject=(* m_pfnCreateObject)();              //函數指針調用   
     return pObject;                                      
}  

有了上面兩個函數,我們在程序執行的時候調用,就可以動態創建對象了。

簡單實現動態創建:

       我們還可以更簡單地實現動態創建,大家注意到,就是在我們的程序類里面有一個RUNTIME_CLASS(class_name)宏,作用就是得到類的RunTime信息,即返回class_name所屬CRuntimeClass的對象。這個宏在MFC里定義為:

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

       在我們的應用程序類(CMyWinApp)的InitInstance()函數下面的CSingleDocTemplate函數中,有:

RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMainFrame),       // main SDI frame window
RUNTIME_CLASS(CMyView)

構造文檔模板的時候就用這個宏得到文檔、框架和視的RunTime信息。有了RunTime信息,我們只要一條語句就可以動態創建了,如:

classMyView->CreateObject();      //對象直接調用用CRuntimeClass本身的CreateObject()

總結:

       最后再總結和明確下動態創建的具體步驟:

  1. 定義一個不帶參數的構造函數(默認構造函數);因為我們是用CreateObject()動態創建,它只有一條語句就是return new XXX,不帶任何參數。所以我們要有一個無參構造函數。
  2. 類說明中使用DECLARE_DYNCREATE(CLASSNMAE)宏;和在類的實現文件中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏;這個宏完成構造CRuntimeClass對象,並加入到鏈表中。
  3. 使用時先通過宏RUNTIME_CLASS得到類的RunTime信息,然后使用CRuntimeClass的成員函數CreateObject創建一個該類的實例。
  4. CObject* pObject = pRuntimeClass->CreateObject();//完成動態創建。

文檔永久保存(串行化、序列化)

  我們可以利用CArchive類將對象數據保存到永久設備上,這樣,即使應用程序關閉,我們也可以將從磁盤文件中讀取對象數據,然后在內存中重新構建相應的對象,這種讓對象數據持久性的過程,即MFC的連續存儲機制稱之為序列化(Serialize)

  MFC文檔的序列化過程包括:創建空文檔、打開文檔、保存文檔和關閉文檔四個操作。

  從單文檔的序列化過程可以看出:打開和保存文檔時,系統都會調用Serialize函數。事實上,MFC AppWizard在創建文檔應用程序框架時已在文檔類中重載了Serialize函數,通過在該函數中添加代碼可達到實現數據序列化的目的。

消息映射消息傳遞


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM