日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MFC六大核心机制之一:MFC程序的初始化

發布時間:2024/4/11 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MFC六大核心机制之一:MFC程序的初始化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?很多做軟件開發的人都有一種對事情刨根問底的精神,例如我們一直在用的MFC,很方便,不用學太多原理性的知識就可以做出各種窗口程序,但喜歡鉆研的朋友肯定想知道,到底微軟幫我們做了些什么,讓我們在它的框架下可以簡單的寫程序。本文開始就跟大家分享一位同行前輩寫的MFC核心機制分析(稍作整理),語言樸實易懂,在讀完此深入淺析的剖析系列后,相信留給大家的是對MFC運行機制的深入理解。

???????MFC六大核心機制概述

?????? 我們選擇了C++,主要是因為它夠藝術、夠自由,使用它我們可以實現各種想法,而MFC將多種可靈活使用的功能封裝起來,我們豈能忍受這種“黑盒”操作?于是研究分析MFC的核心機制成為必然。

?? ??? 首先,列出要講的MFC六大核心機制:

?? ??? 1、MFC程序的初始化。
?? ??? 2、運行時類型識別(RTTI)。
?? ??? 3、動態創建。
?? ??? 4、永久保存。
?? ??? 5、消息映射。
??? ?? 6、消息傳遞。

?????? 本文講第一部分,MFC程序的初始化過程。

???????簡單的MFC窗口程序

?????? 設計一個簡單完整MFC程序,產生一個窗口。當然這不能讓AppWizard自動為我們生成。我們可以在Win32 Application工程下面那樣寫:

C++代碼
  • #include?<afxwin.h>???
  • class?MyApp?:?public?CWinApp ??
  • { ??
  • public: ??
  • BOOL?InitInstance()??//②程序入點???
  • { ??
  • ??CFrameWnd?*Frame=new?CFrameWnd();//構造框架???
  • ??m_pMainWnd=Frame;?//將m_pMainWnd設定為Frame;???
  • ??Frame->Create(NULL,"最簡單的窗口");//建立框架???
  • ??Frame->ShowWindow(SW_SHOW);??//顯示框架???
  • ??return?true;?????????//返回???
  • ?}???
  • }; ??
  • MyApp?theApp;??//①建立應用程序。??
  • ?????? 設定鏈接MFC庫,運行,即可看見一個窗口。

    ?????? 從上面,大家可以看到建立一個MFC窗口很容易,只用兩步:一是從CWinApp派生一個應用程序類(這里是MyApp),然后建立應用程序對象(theApp),就可以產生一個自己需要的窗口(即需要什么樣就在InitInstance()里創建就行了)。

    ?????? 整個程序,就改寫一個InitInstance()函數,創建那么一個對象(theApp),就是一個完整的窗口程序。這就是“黑盒”操作的魔力!

    ?????? 在我們正想為微軟鼓掌的時候,我們突然覺得心里空蕩蕩的,我們想知道微軟幫我們做了什么事情,而我們想編自己的程序時又需要做什么事情,哪怕在上面幾行的程序里面,我們還有不清楚的地方,比如,干嘛有一個m_pMainWnd指針變量,它從哪里來,又要到哪里去呢?想一想在DOS下編程是多么美妙的一件事呵,我們需要什么變量,就聲明什么變量,需要什么樣的函數,就編寫什么樣的函數,或者引用函數庫……但是現在我們怎么辦?

    ?????? 我們可以逆向思維一下,MFC要達到這種效果,它是怎么做的呢?首先我們要弄明白,VC++不是一種語言,它就象我們學c語言的時候的一個類似記事本的編輯器(請原諒我的不貼切的比喻),所以,在VC里面我們用的是C++語言編程,C++才是根本(初學者總是以為VC是一門什么新的什么語言,一門比C++先進很多的復雜語言,汗)。說了那么多,我想用一句簡單的話概括“MFC黑箱’,就是為我們的程序加入一些固化的‘C++代碼’的東西”。

    ?????? 既然MFC黑箱幫我們加入了代碼,那么大家想想它會幫我們加入什么樣的代碼呢?他會幫我們加入求解一元二次方程的代碼嗎?當然不會,所以它加入的實際上是每次編寫窗口程序必須的,通用的代碼。

    ?????? 再往下想,什么才是通用的呢?我們每次視窗編程都要寫WinMain()函數,都要有注冊窗口,產生窗口,消息循環,回調函數……即然每次都要的東西,就讓它們從我們眼前消失,讓MFC幫忙寫入!

    ???????手動模擬MFC程序的初始化??????

    ?????? 要知道MFC初始化過程,大家當然可以跟蹤執行程序。但這種跟蹤很麻煩,我相信大家都會跟蹤的暈頭轉向。本人覺得哪怕你理解了MFC代碼,也很容易讓人找不著北,我們完全不懂的時候,在成千上萬行程序的迷宮中如何能找到出口?

    ?????? 我們要換一種方法,不如就來重新編寫個MFC庫吧,嘩!大家不要笑,小心你的大牙,我不是瘋子(雖然瘋子也說自己不瘋)。我們要寫的就是最簡單的MFC類庫,就是把MFC宏觀上的,理論上的東西寫出來。我們要用最簡化的代碼,簡化到剛好能運行。

    ???????1、需要“重寫”的MFC庫

    ???????既然,我們這一節寫的是MFC程序的初始化過程,上面我們還有了一個可執行的MFC程序。程序中只是用了兩個MFC類,一個是CWinApp,另一個是CFrameWnd。當然,還有很多同樣重要MFC類如視圖類,文檔類等等。但在上面的程序可以不用到,所以暫時省去了它(總之是為了簡單)。

    ?????? 好,現在開始寫MFC類庫吧……唉,面前又有一個大難題,就是讓大家背一下MFC層次結構圖。天,那張魚網怎么記得住,但既然我們要理解他,總得知道它是從那里派生出來的吧。

    ?????? 考慮到大家都很辛苦,那我們看一下上面兩個類的父子關系(箭頭代表派生):

    ???????CObject->CCmdTarget->CWinThread->CWinApp->自己的重寫了InitInstance()的應用程序類。
    ?????? CObject(同上)->CCmdTarget(同上)->CWnd->CFrameWnd

    ?????? 看到層次關系圖之后,終于可以開始寫MFC類庫了。按照上面層次結構,我們可以寫以下六個類(為了直觀,省去了構造函數和析構函數)。

    C++代碼
  • /???
  • class?CObiect{};//MFC類的基類。???
  • class?CCmdTarget?:?public?CObject{}; ??
  • ------------------------------------------------ ??
  • class?CWinThread?:?public?CCmdTarget{}; ??
  • class?CWinApp?:?public?CWinThread{}; ??
  • ------------------------------------------------ ??
  • class?CWnd?:?public?CCmdTarget{}; ??
  • class?CFrameWnd?:?public?CWnd{}; ??
  • /??
  • ?????? 大家再想一下,在上面的類里面,應該有什么?大家馬上會想到,CWinApp類或者它的基類CCmdTarget里面應該有一個虛函數virtual BOOL InitInstance(),是的,因為那里是程序的入口點,初始化程序的地方,那自然少不了的。可能有些朋友會說,反正InitInstance()在派生類中一定要重載,我不在CCmdTarget或CWinApp類里定義,留待CWinApp的派生類去增加這個函數可不可以。扯到這個問題可能有點越說越遠,但我想信C++的朋友對虛函數應該是沒有太多的問題的。總的來說,作為程序員如果清楚知道基類的某個函數要被派生類用到,那定義為虛函數要方便很多。

    ?????? 也有很多朋友問,C++為什么不自動把基類的所有函數定義為虛函數呢,這樣可以省了很多麻煩,這樣所有函數都遵照派生類有定義的函數就調用派生類的,沒定義的就調用基類的,不用寫virtual的麻煩多好!其實,很多面向對象的語言都這樣做了。但定義一個虛函數要生成一個虛函數表,要占用系統空間,虛函數越多,表就越大,有時得不償失!這里哆嗦幾句,是因為往后要說明的消息映射中大家更加會體驗到這一點,好了,就此打往。

    ?????? 上面我們自己解決了一個問題,就是在CCmdTarge寫一個virtual BOOL InitInstance()。

    ???????2、WinMain()函數和CWinApp類

    ?????? 大家再往下想,我們還要我們MFC“隱藏”更多的東西:WinMain()函數,設計窗口類,窗口注冊,消息循環,回調函數……我們馬上想到封裝想封裝他們。大家似乎隱約地感覺到封裝WinMain()不容易,覺得WinMain()是一個特殊的函數,許多時候它代表了一個程序的起始和終結。所以在以前寫程序的時候,我們寫程序習慣從WinMain()的左大括寫起,到右大括弧返回、結束程序。

    ?????? 我們換一個角度去想,有什么東西可以拿到WinMain()外面去做,許多初學者們,總覺得WinMain()函數是天大的函數,什么函數都好象要在它里面才能真正運行。其實這樣了解很片面,甚至錯誤。我們可以寫一個這樣的C++程序:

    C++代碼
  • ???
  • #include?<iostream.h>???
  • class?test{ ??
  • public: ??
  • ?test(){cout<<"請改變你對main()函數的看法!"<<endl;} ??
  • }; ??
  • test?test1; ??
  • /**************************/??
  • void?main(){} ??
  • ??
  • ?????? 在上面的程序里,入口的main()函數表面上什么也不做,但程序執行了(注:實際入口函數做了一些我們可以不了解的事情),并輸出了一句話(注:全局對象比main()首先運行)。現在大家可以知道我們的WinMain()函數可以什么都不做,程序依然可以運行,但沒有這個入口函數程序會報錯。

    ?????? 那么WinMain()函數會放哪個類上面呢,請看下面程序:

    C++代碼
  • #include?<afxwin.h>???
  • class?MyApp?:?public?CWinApp ??
  • { ??
  • public: ??
  • ?BOOL?InitInstance()??//②程序入點???
  • ?{ ??
  • ??AfxMessageBox("程序依然可以運行!"); ??
  • ??return?true; ??
  • ?} ??
  • }; ??
  • ??
  • MyApp?theApp;??//①建立應用程序。??
  • ?????? 大家可以看到,我并沒有構造框架,而程序卻可以運行了——彈出一個對話框(如果沒有WinMain()函數程序會報錯)。上面我這樣寫還是為了直觀起見,其實我們只要寫兩行程序:

    ?????? #include <afxwin.h>
    ?????? CWinApp theApp;???? //整個程序只構造一個CWinApp類對象,程序就可以運行!

    ?????? 所以說,只要我們構造了CWinApp對象,就可以執行WinMain()函數。我們馬上相信WinMain()函數是在CWinApp類或它的基類中,而不是在其他類中。其實這種看法是錯誤的,我們知道編寫C++程序的時候,不可能讓你在一個類中包含入口函數,WinMain()是由系統調用,跟我們的平時程序自身調用的函數有著本質的區別。我們可以暫時簡單想象成,當CWinApp對象構造完的時候,WinMain()跟著執行。

    ?????? 現在大家明白了,大部分的“通用代碼(我們想封裝隱藏的東西)”都可以放到CWinApp類中,那么它又是怎樣運行起來的呢?為什么構造了CWinApp類對象就“自動”執行那么多東西。

    ?????? 大家再仔細想一下,CWinApp類對象構造之后,它會“自動”執行自己的構造函數。那么我們可以把想要“自動”執行的代碼放到CWinApp類的構造函數中。

    ?????? 那么CWinApp類可能打算這樣設計(先不計較正確與否):

    C++代碼
  • class?CWinApp?:?public?CWinThead{ ??
  • public: ??
  • virtual?BOOL?InitInstance();?//解釋過的程序的入點???
  • ??CWinApp?::CWinApp(){???//構造函數???
  • ??????
  • ???WinMain();???//這個是大家一眼看出的錯誤???
  • ???Create();????//設計、創建、更新顯示窗口???
  • ???Run();?????//消息循環???
  • ???//???
  • } ??
  • };??
  • ???????寫完后,大家又馬上感覺到似乎不對,WinMain()函數在這里好象真的一點用處都沒有,并且能這樣被調用嗎(請允許我把手按在圣經上聲明一下:WinMain()不是普通的函數,它要肩負著初始化應用程序,包括全局變量的初始化,是由系統而不是程序本身調用的,WinMain()返回之后,程序就結束了,進程撤消)。再看Create()函數,它能確定設計什么樣的窗口,創建什么樣的窗口嗎?如果能在CWinApp的構造函數里確定的話,我們以后設計MFC程序時窗口就一個樣,這樣似乎不太合理。

    ???????回過頭來,我們可以讓WinMain()函數一條語句都不包含嗎?不可以,我們看一下WinMain() 函數的四個參數:

    ?????? WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

    ?????? 其中第一個參數指向一個實例句柄,我們在設計WNDCLASS的時候一定要指定實例句柄。我們窗口編程,肯定要設計窗口類。所以,WinMain()再簡單也要這樣寫:

    ?????? int WinMain(HINSTANCE hinst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    ?????? {? hInstance=hinst }

    ?????? 既然實例句柄要等到程序開始執行才能知道,那么我們用于創建窗口的Create()函數也要在WinMain()內部才能執行(因為如果等到WinMain()執行完畢后,程序結束,進程撤消,當然Create()也不可能創建窗口)。

    ?????? 再看Run()(消息循環)函數,它能在WinMain()函數外面運行嗎?眾所周知,消息循環就是相同的那么幾句代碼,但我們也不要企圖把它放在WinMain()函數之外執行。

    ?????? 所以我們的WinMain()函數可以像下面這樣寫:

    ?????? WinMain(……)
    ?????? {
    ????????????? ……窗口類對象執行創建窗口函數……
    ????????????? ……程序類對象執行消息循環函數……
    ?????? }

    ?????? 對于WinMain()的問題,得總結一下,我們封裝的時候是不可以把它封裝到CWinApp類里面,但由于WinMain()的不變性(或者說有規律可循),MFC完全有能力在我們構造CWinApp類對象的時候,幫我們完成那幾行代碼。

    ?????? 轉了一個大圈,我們仿佛又回到了SDK編程的開始。但現在我們現在能清楚地知道,表面上MFC與SDK編程截然不同,但實質上MFC只是用類的形式封裝了SDK函數,封裝之后,我們在WinMain()函數中只需要幾行代碼,就可以完成一個窗口程序。我們也由此知道了應如何去封裝應用程序類(CWinApp)和主框架窗口類(CFrameWnd)。下面把上開始設計這兩個類。

    ???????3、MFC庫的“重寫”

    ?????? 為了簡單起見,我們忽略這兩個類的基類和派生類的編寫,可能大家會認為這是一種很不負責任的做法,但本人覺得這既可減輕負擔,又免了大家在各類之間穿來穿去,更好理解一些(我們在關鍵的地方作注明)。還有,我把全部代碼寫在同一個文件中,讓大家看起來不用那么吃力,但這是最不提倡的寫代碼方法,大家不要學哦!

    C++代碼
  • #include?<windows.h>???
  • HINSTANCE?hInstance; ??
  • ??
  • class?CFrameWnd?? ??
  • { ??
  • ?HWND?hwnd; ??
  • public: ??
  • ?CFrameWnd();???//也可以在這里調用Create()???
  • ?virtual?~CFrameWnd(); ??
  • ?int?Create();????//類就留意這一個函數就行了!???
  • ?BOOL?ShowWnd(); ??
  • }; ??
  • class?CWinApp1?? ??
  • { ??
  • public: ??
  • ?CFrameWnd*?m_pMainWnd;//在真正的MFC里面???
  • //它是CWnd指針,但這里由于不寫CWnd類???
  • //只要把它寫成CFrameWnd指針???
  • ?CWinApp1*?m_pCurrentWinApp;//指向應用程序對象本身???
  • ?CWinApp1(); ??
  • ?virtual?~CWinApp1(); ??
  • ?virtual?BOOL?InitInstance();//MFC原本是必須重載的函數,最重要的函數!!!!??
  • ?virtual?BOOL?Run();//消息循環???
  • }; ??
  • CFrameWnd::CFrameWnd(){} ??
  • CFrameWnd::~CFrameWnd(){} ??
  • ??
  • int?CFrameWnd::Create()???//封裝創建窗口代碼???
  • { ??
  • ?WNDCLASS?wndcls; ??
  • ?wndcls.style=0; ??
  • ?wndcls.cbClsExtra=0; ??
  • ?wndcls.cbWndExtra=0; ??
  • ?wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); ??
  • ?wndcls.hCursor=LoadCursor(NULL,IDC_CROSS); ??
  • ?wndcls.hIcon=LoadIcon(NULL,IDC_ARROW); ??
  • ?wndcls.hInstance=hInstance; ??
  • ?wndcls.lpfnWndProc=DefWindowProc;//默認窗口過程函數。???
  • //大家可以想象成MFC通用的窗口過程。???
  • ?wndcls.lpszClassName="窗口類名"; ??
  • ?wndcls.lpszMenuName=NULL; ??
  • ?RegisterClass(&wndcls); ??
  • ??
  • ?hwnd=CreateWindow("窗口類名","窗口實例標題名",WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL); ??
  • ??return?0; ??
  • } ??
  • ??
  • BOOL?CFrameWnd::ShowWnd()//顯示更新窗口???
  • { ??
  • ?ShowWindow(hwnd,SW_SHOWNORMAL); ??
  • ?UpdateWindow(hwnd); ??
  • ?return?0; ??
  • } ??
  • ??
  • /???
  • CWinApp1::CWinApp1() ??
  • { ??
  • ?m_pCurrentWinApp=this; ??
  • } ??
  • CWinApp1::~CWinApp1(){} ??
  • //以下為InitInstance()函數,MFC中要為CWinApp的派生類改寫,???
  • //這里為了方便理解,把它放在CWinApp類里面完成!???
  • //你只要記住真正的MFC在派生類改寫此函數就行了。???
  • BOOL?CWinApp1::InitInstance() ??
  • { ??
  • ?m_pMainWnd=new?CFrameWnd; ??
  • ?m_pMainWnd->Create(); ??
  • ?m_pMainWnd->ShowWnd(); ??
  • ?return?0; ??
  • } ??
  • ??
  • BOOL?CWinApp1::Run()//封裝消息循環???
  • { ??
  • ?MSG?msg; ??
  • ?while(GetMessage(&msg,NULL,0,0)) ??
  • ?{ ??
  • ??TranslateMessage(&msg); ??
  • ??DispatchMessage(&msg); ??
  • ?} ??
  • ?return?0; ??
  • }?//封裝消息循環???
  • ??
  • CWinApp1?theApp;???//應用程序對象(全局)???
  • ??
  • int?WINAPI?WinMain(?HINSTANCE?hinst,?HINSTANCE?hPrevInstance,???LPSTR?lpCmdLine,??int?nCmdShow) ??
  • { ??
  • ?hInstance=hinst; ??
  • ?CWinApp1*?pApp=theApp.m_pCurrentWinApp; ??
  • //真正的MFC要寫一個全局函數AfxGetApp,以獲取CWinApp指針。???
  • ?pApp->InitInstance(); ??
  • ?pApp->Run(); ??
  • ?return?0; ??
  • }??
  • ?????? 代碼那么長,實際上只是寫了三個函數,一是CFrameWnd類的Create(),第二個是CWinApp類的InitInstance()和Run()。在此特別要說明的是InitInstance(),真正的MFC中,那是我們跟據自己構造窗口的需要,自己改寫這個函數。

    ?????? 大家可以看到,封裝了上面兩個類以后,在入口函數WinMain中就寫幾行代碼,就可以產生一個窗口程序。在MFC中,因為WinMain函數就是固定的那么幾行代碼,所以MFC絕對可以幫我們自動完成(MFC的特長就是幫我們完成有規律的代碼),也因此我們創建MFC應用程序的時候,看不到WinMain函數。


    總結

    以上是生活随笔為你收集整理的MFC六大核心机制之一:MFC程序的初始化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。