不少作軟件開發的人都有一種對事情刨根問底的精神,例如咱們一直在用的MFC,很方便,不用學太多原理性的知識就能夠作出各類窗口程序,但喜歡鑽研的朋友確定想知道,到底微軟幫咱們作了些什麼,讓咱們在它的框架下能夠簡單的寫程序。本文開始就跟你們分享一位同行前輩寫的MFC核心機制分析(稍做整理),語言樸實易懂,在讀完此深刻淺析的剖析系列後,相信留給你們的是對MFC運行機制的深刻理解。ios
MFC六大核心機制概述程序員
咱們選擇了C++,主要是由於它夠藝術、夠自由,使用它咱們能夠實現各類想法,而MFC將多種可靈活使用的功能封裝起來,咱們豈能忍受這種「黑盒」操做?因而研究分析MFC的核心機制成爲必然。web
首先,列出要講的MFC六大核心機制:編程
一、MFC程序的初始化。
二、運行時類型識別(RTTI)。
三、動態建立。
四、永久保存。
五、消息映射。
六、消息傳遞。app
(一)MFC程序的初始化框架
通常狀況下,採用文檔/視圖結構的應用程序至少應由如下對象組成:應用程序對象(CWinApp類派生的對象),框架窗口對象(CFrameWnd類派生的對象),文檔對象(CDocument類派生的對象),視圖對象(CView類派生的對象)。另外,還必須有一個負責管理文檔和視圖的文檔模板類(CDocTemplate)。其中的主角是CDocument類(文檔類)和 CView類(視圖類),這就是文檔/視圖結構的由來。各種的做用分別介紹以下:函數
1.CWinApp工具
CwinApp (應用程序類) 提供了用戶與 Windows 應用程序之間進行交流的界面。在實例化該類對象後,這個對象自動地把自身與 Widnows 系統創建聯繫,接收 Windows 傳送的消息,並交給程序中相應的對象去處理,免去了程序員許多的工做,使得開發 Windows 序變得簡單方便。
這個類中有一個重要的成員函數:InitInstance(),在 Windows 環境下,能夠運行同一程序的多個實例,函數 InitInstance() 的做用是在生成的一個新的實例的時候,完成一些初始化的工做。另外還有一個函數InitApplication(),與前者的區別是它"每個程序只調用一次",而 InitInstance() 是"每個例程調用一次"。
this
2.CFrameWndspa
CFrameWnd (框架窗口類) 是應用程序的框架窗口。所謂框架窗口是指包括菜單、工具欄、狀態欄和窗口客戶區的整個應用程序的主窗口,至關於簡單應用程序框架中所提到的主窗口。在 MFC 程序中,通常並不須要常常對 CFrameWnd 類進行操做,更多的是對視窗類進行操做,達到對程序中的數據進行編輯的目的。
3.CView
CView (視圖類) 派生於 Cwnd 類,用於管理文檔/視圖結構中的窗口客戶區,這個窗口在文檔/視圖結構中稱爲視圖。視圖類專門用於對應用程序的數據進行顯示,在視圖類中有一個很重要的函數 OnDraw(),OnDraw()函數是用於進行應用程序數據顯示的函數,通常在派生類中要重寫這一個函數。在文檔/視圖結構中,重寫的OnDraw()函數首先清空客戶區窗口,而後再在窗口上繪製客戶須要的內容,也就是說,OnDraw() 函數將集中管理全部的數據顯示工做。
4.CDocument
CDocument (文檔類) 雖然視圖類負責應用程序數據的顯示,但應用程序的數據通常不直接由視圖類管理,而是做爲文檔類(CDocument)的數據成員,由文檔類來進行集中管理,並且文檔類將直接與磁盤相聯繫,把文檔類中的數據存盤,或從磁盤中取出存盤的數據。視圖類用 OnDraw() 函數展現數據,但應用程序的數據卻存放在文檔類中,視圖類的函數 GetDocument() 的返回值就是指向文檔類的指針,經過這個指針就能夠訪問 到文檔類中的公有數據成員。文檔類的數據要存盤或取盤要與磁盤進行數據傳遞,能夠用 CFile 類結合CFileDialog 類實現。在文檔/視圖結構中,經過文檔類中的序列化函數 Serialize() 就能夠很簡單的完成數據存取任務。文檔/視圖結構中,數據的傳輸過程以下圖所示:
5.CDocTemplate
CDocTemplate(文檔模板類)的做用是鏈接文檔/視圖結構中文檔類,視圖類和框架窗口類之間的關係,文檔類,視圖類和框架窗口類之間的關係是在文檔模板類中創建起來的,同時文檔模板類還加載了菜單以及與菜單資源使用的 ID 等資源。具體來講,在 CWinApp 派生類的 InitInstance() 函數中創建了文檔模板類 CDocTemplate,並用文檔模板類鏈接資源、框架窗口、文檔和視圖。
6.類層次結構
繼承類圖,左邊爲父類,右邊爲派生類。實線框中的類只有 CObject 在 "afx.h" 中,其他都在 "afxwin.h" 中,虛線框中所有是用戶本身定義的派生類。
7.使用控制檯程序模擬 MFC 初始化
注意!是使用控制檯應用程序(Win32 Console Application)模擬而不是 MFC 程序!另外不用急着 copy 代碼,我在文章底部附錄一給出了代碼下載連接和使用方法。
假設新建一個控制檯應用程序叫"My",首先天然是編寫上面五個重要類的派生類,其中 CDocTemplate 用於管理其餘類的對象,能夠被直接使用,這裏就暫且不討論了(它的派生類被聲明在"afxwin.h"中)。因而建立四個頭文件並聲明其餘類的派生類。
//My.h class CMyApp : public CWinApp { public: CMyApp(); virtual BOOL InitInstance(); //覆蓋 };
//MyDoc.h class CMyDoc : public CDocument { public : CMyDoc(); };
//MyFrame.h class CMyFrame : public CFrameWnd { public: CMyFrame(); };
//MyView.h class CMyView : public CView { public: CMyView(); };
這就是客戶須要建立的全部頭文件了,當你使用 IDE 建立一個 MFC 程序時,他會自動爲你建立上述頭文件。另外還須要注意下面這 3 個頭文件,它們在 MFC 類庫中是真實存在的,而且名稱相同。"afx.h" 聲明瞭 CObject 基類,"afxwin.h" 聲明瞭 MFC 中使用的大部分類的基類,"stdafx.h" 是爲了減小重複編譯設置的,用於創建一個預編譯的頭文件 .PCH 和一個預約義的類型文件 STDAFX.OBJ。因爲MFC體系結構很是大,包含許多頭文件,若是每次都編譯的話比較費時,所以咱們把經常使用的 MFC頭 文件都放在 stdafx.h 中,如 afxwin.h、afxext.h、afxdisp.h、afxcmn.h 等,而後讓 stdafx.cpp 包含這個 stdafx.h 文件。這樣,因爲編譯器能夠識別哪些文件已經編譯過,因此stdafx.cpp就只編譯一次,並生成所謂的預編譯頭文件。
//stdfx.h #include "afxwin.h" //其餘必要的頭文件 //#include <......> //#include <......> //#include <......> //連接必要的庫 //#pragma comment(......) //#pragma comment(......) //#pragma comment(......)
//afxwin.h #pragma once #include "afx.h" //CCmdTarget類聲明 class CCmdTarget : public CObject { public: CCmdTarget(); }; //CDocument類聲明 class CDocument : public CCmdTarget { public: CDocument(); }; //CWnd類聲明 class CWnd : public CCmdTarget { public: CWnd(); virtual BOOL Create(); BOOL CreateEx(); virtual BOOL PreCreateWindow(); }; //CFrameWnd類聲明 class CFrameWnd : public CWnd { public: CFrameWnd(); }; //CView類聲明 class CView : public CWnd { public: CView(); }; //CWinThread類聲明 class CWinThread : public CCmdTarget { public: CWnd* m_pMainWnd; CWinThread(); virtual BOOL InitInstance(); virtual int Run(); }; //CWinApp類聲明 class CWinApp : public CWinThread { public: CWinApp(); virtual BOOL InitApplication(); virtual BOOL InitInstance(); //覆蓋 virtual int Run(); //覆蓋 };
說明幾點,CWnd 類中有建立窗口的虛函數,Create,CreateEx(前者的擴展版本),PreCreateWindow。 所以實際編寫其派生類時須要覆蓋這些函數。CWinApp 中有重要的 InitApplication() 函數和繼承自 CWinThread 的 InitInstance() 和 Run(),編寫其派生類一般要覆蓋它們。值得一提的是,CWinThread 有一個數據成員 CWnd* m_pMainWnd,它是指向框架窗對象的指針。
//afx.h //演示須要,MFC實際上不包含<iostream> #include <iostream> using namespace std; //實際上下面這些重定義寫在<WinDef.h>中,MFC編程時一般會自動包含該頭文件,爲演示方便就寫這了 typedef int BOOL; #define TRUE 1; //CObect類聲明 class CObject { public: CObject(); };
"afx.h" 中聲明瞭祖宗類 CObject。
//MyDoc.cpp #include "stdafx.h" #include "MyDoc.h" CMyDoc::CMyDoc() { cout<<"CMyDoc Constructor."<<endl; }
//MyFrame.cpp #include "stdafx.h" #include "MyFrame.h" //CMyFrame類方法定義 CMyFrame::CMyFrame() { cout<<"CMyFrame Constructor."<<endl; }
//MyView.cpp #include "stdafx.h" #include "MyView.h" //CMyView類方法定義 CMyView::CMyView() { cout<<"CMyView Constructor."<<endl; }
//My.cpp #include "stdafx.h" #include "My.h" #include "MyFrame.h" #include "MyDoc.h" #include "MyView.h" //CMyWinApp類方法定義 CMyApp::CMyApp() { cout<<"CMyApp Constructor."<<endl; } BOOL CMyApp::InitInstance() //覆蓋 { cout<<"CMyApp::InitInstance()."<<endl; //下面的註釋爲 MFC 源碼中的內容,使用RTTI實例化了CMyDoc、 //CMyFrame、CMyView,而且使用 CDocTemplate 類來鏈接管理 /* // 註冊應用程序的文檔模板。文檔模板 // 將用做文檔、框架窗口和視圖之間的鏈接 CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMyFrame), // 主 SDI 框架窗口 RUNTIME_CLASS(CMyView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); */ this->m_pMainWnd = new CMyFrame(); return TRUE; } //全局變量 CMyApp theApp; int main() { theApp.InitApplication(); theApp.InitInstance(); theApp.Run(); return 0; }
說明幾點,InitInstance() 中的註釋是 MFC 中的源碼,我在這裏貼出來是爲了讓你們理解 MFC 是在這個時候實例化了 CMyDoc、CMyFrame 和 CMyView,至於 RUNTIME_CLASS 是什麼,下幾章博客會詳細介紹。
注意!最重要的東西來了!"My.cpp" 中有一個很關鍵的全局對象 CMyApp theApp,它是整個 MFC 初始化的關鍵。因爲 C++ 中全局對象的構建將比程序進入點(DOS 環境爲 main,Windows 爲 WinMain)更早,所以 CMyApp 的構造函數最早被執行,完成一系列的初始化。注意到該演示程序裏有 main() 函數,可是 MFC 程序中並無(Window 程序主函數實際上是 WinMain()),那麼它去哪了呢?MFC 將 WinMain() 封裝了起來,在 CMyApp theApp 實例化後會自動調用 WinMain() 函數並得到 theApp 對象的指針對其操做,讓使用者看起來 theApp 纔是程序的入口點。
而後是剩餘的存在於MFC中 .cpp 文件:
//stdfx.cpp #include "stdafx.h"
//afx.cpp #include "afx.h" //CObject類聲明 CObject::CObject() { cout<<"CObject Constructor."<<endl; }
//afxwin.cpp #include "afxwin.h" //這裏導入"CMyApp.h"是爲了使用 theApp 全局變量以使用改造版的 AfxGetApp() #include "My.h" extern CMyApp theApp; //CCmdTarget類方法定義 CCmdTarget::CCmdTarget() { cout<<"CCmdTarget Constructor."<<endl; } //CDocument類方法定義 CDocument::CDocument() { cout<<"CDocument Constructor."<<endl; } //CWnd類方法定義 CWnd::CWnd() { cout<<"CWnd Constructor."<<endl; } BOOL CWnd::Create() { cout<<"CWnd::Create()."<<endl; return TRUE; } BOOL CWnd::CreateEx() { cout<<"CWnd::CreateEx()."<<endl; return TRUE; } BOOL CWnd::PreCreateWindow() { cout<<"CWnd::PreCreateWindow()."<<endl; return TRUE; } //CFrameWnd類方法定義 CFrameWnd::CFrameWnd() { cout<<"CFrameWnd Constructor."<<endl; } //CView類方法定義 CView::CView() { cout<<"CView Constructor."<<endl; } //CWinThread類方法定義 CWinThread::CWinThread() { cout<<"CWinThread Constructor."<<endl; } BOOL CWinThread::InitInstance() { cout<<"CWinThread::InitInstance()."<<endl; return TRUE; } int CWinThread::Run() { cout<<"CWinThread::Run()."<<endl; return 1; } //CWinApp類方法定義 CWinApp::CWinApp() { cout<<"CWinApp Constructor."<<endl; } BOOL CWinApp::InitInstance() { cout<<"CWinApp::InitInstance()."<<endl; return TRUE; } BOOL CWinApp::InitApplication() { cout<<"CWinApp::InitApplication()."<<endl; return TRUE; } int CWinApp::Run() { cout<<"CWinApp::Run()."<<endl; return 1; }
值得注意的是 InitInstance() 虛函數是在 CWinThread 中被聲明,InitApplication() 則是在 CWinApp中被聲明。
CObject Constructor.
CCmdTarget Constructor.
CWinThread Constructor.
CWinApp Constructor.
CMyApp Constructor.
CWinApp::InitApplication().
CMyApp::InitInstance().
CObject Constructor.
CCmdTarget Constructor.
CWnd Constructor.
CFrameWnd Constructor.
CMyFrame Constructor.
CWinApp::Run().
代碼我已經上傳,編譯一下就能夠 MyMFC.zip
下一篇我會教你們怎麼樣寫出一個好看的窗體
https://blog.csdn.net/raito__/article/details/51699828