文章目錄
習慣的思維
用習慣了C的人要看一個程式時首先會想到找到那個main函數在哪,然後再順著往下看.因為main函數作為程式的進入點,整個程式都是從那開始執行的.當在C++中SDK(win32 API project)開發時也繼承沿用C的思維,是有個main函數,不過現在的main函數改名字了,叫WinMain,當然有時還有變體,比如叫_tWinMain,反正名字中總會帶個Main,讓我們一看就知道.而在QT中就跟C一樣,就老實的來個標準的main函數.
我們會發現C++中可以有一個單獨的main函數,不用包含在哪個類中,另外還有不屬於任何類的全域變數或全域函數這自然就不是純粹的物件導向語言了.所以說C++支援多種編程範式嘛,可以是跟C完全一樣的面向過程範式,或者再加些普通的類就是基於對象的範式了,如果再用到繼承和多態就是物件導向了,而要是用到模板就是泛型範式了.而且這些範式可以互相混合用.而C#就是純的物件導向,所以它裡面雖然也有main函數,但也是要放在一個類裡面去,至於具體放哪個類無所謂,你隨便放.一般預設是放Program這個類裡.當然並不是說純的物件導向就比混合的範式好,應該各有優缺點.
哎扯得有點遠了,言歸正傳.
SDK中的流程
開發一個帶介面的SDK程式大致流程是這樣的.首先自然是要有個main函數做進入點.然後按下面的步驟來(為了討論方便,只說大概流程,代碼也是不完整的)
int _tWinain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
InitApplicatio(HINSTANCE hInstance) //第1步,註冊表單類,並在這裡指定了表單過程WndProc
InitInstance(HINSTANCE hInstance, int nCmdShow) //第2步,建立表單
while (GetMessage(&msg, NULL, 0, 0)) //第3步訊息迴圈,指派訊息
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam; //第4步,退出程式
}
BOOL InitApplicatio(HINSTANCE hInstance)
{
return RegisterClass(...);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
CreateWindow(...); //建立表單
ShowWindow(...); //顯示表單
UpdateWindow(...); //送出WM_PAINT
return TRUE;
}
LRESULT CALLBACK WndProc(...){ }
在MFC中產生一個有介面的程式大體過程也一樣,只不過封裝起來了.那我們感興趣的就是兩個問題.
1.MFC中有沒有main函數了,如果有它跑哪去了?
2.如果有main函數,它裡面的那4步涉及到的具體操作是否也跟win32 API一樣?
下面我們就來一一解答下
MFC封裝背後流程
實際上候捷那本深入淺出MFC裡面有講的很清楚了.不過由於講的太詳細了,有幾十頁,看的容易暈,而且他舉的例都是老版本的MFC類,在新版本中一些類的函數會有一點點變化.
我這裡就只概括的講下最簡潔的流程.先假如有類CMyApp繼承自CWinApp吧
1.針對第一個問題,MFC裡是有用到main函數的
// export WinMain to force linkage to this module
extern int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow);
就是這一個函數,在MFC的源檔案appmodul.cpp中能看到這些代碼,那這個main怎麼被MFC調用的呢,你看那注釋,是linkage to this module,也就是被連結器去調用的.準確說是被C-Runtime DLL,C運行時動態連結程式庫調用的.
調用main的順序
我們知道在MFC中能從代碼裡看到的進入點是定義一個全域的繼承於CWinApp的類.比如CMyApp theApp;這樣定義下.在C++中全域變數是先於main被執行的,所以先初始化theApp後才接著調用main
2.針對第2個問題,main函數裡具體的操作.
知道了有main函數,你心裡可能有一絲安慰了.但還是有些覺得不安的是這main函數裡的具體操作是否跟SDK中的一樣,是不是也來那麼幾步,先註冊視窗再建立視窗之類的.
答案是MFC調用的main函數大概流程差不多是那樣,但實現細節很不一樣.我們看下上面說的AfxWinMain裡面的內容是啥吧.你可以在winmain.cpp中看到詳細代碼.
把這個main函數簡化一下,做的操作大概是這樣,
AfxWinMain(...)
{
//先通過一個全域函數獲得CWinApp和CWinThread的指標,因為調用main之前已經初始化了這兩個類.CMyApp初始化時也會初始化他的父類CWinApp,及父類的父類CWinThread
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
//這下面幾個函數就差不多是完成前面講的SDK中的所有步驟
pApp->InitApplication();
pThread->InitInstance();
pThread->Run();
AfxWinTerm(); //結束程式
}
反正結束程式我們就不用管了,重點關心前面的三步,註冊視窗,建立視窗,還有訊息指派.
前面的SDK程式中也恰好有函數InitApplication 註冊視窗, InitInstance建立並顯示視窗.而Run函數你猜想可能是指派訊息的..其實大體思路還是沒錯,但實現細節還是有蠻多區別.
pApp->InitApplication();這函數實際上並沒有註冊視窗.註冊視窗,建立顯示視窗全是在pThread->InitInstance();這函數中完成,InitInstance是個虛函數,而且我們在自己的代碼中會重寫它.所以最後調用的是我們自己寫的那個InitInstance函數,這就是物件導向裡多態的功能了啊.你指標最終指向對應的子類定義的函數.
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMyFrameWnd; //這張操作會註冊並建立視窗,m_pMainWnd就是返回的視窗控制代碼
m_pMainWnd->ShowWindow(m_nCmdShow); //顯示視窗
m_pMainWnd->UpdateWindow();
}
pThread->Run();是指派訊息,你可以在thrdcore.cpp中查看CWinThreed的run函數的源碼,下面摘了一點點.
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{}
不過關於訊息的處理MFC用到了訊息映射機制,比如複雜.這裡不討論了,反正大概就把CMyFrameWnd當成是視窗過程就行了.
MFC怎麼封裝CreateWindow見:
http://blog.csdn.net/weiwenhp/article/details/8796337
總結起來可以這樣簡單的說,MFC中有main函數,但是由系統去調用.然後main函數裡面執行的操作差不多,只不過它是通過CWinApp和CWinThread的指標去調用一些相關的函數.而指標嘛由於調用了虛函數,所以用到了物件導向中的多態,於是轉來轉去的.然後最難的地方可能就是訊息機制在這裡更複雜一點了.不能簡單的與SDK中做一對一的對比.