窗口类、窗口类对象与窗口 三者之间关系
本文摘自孫鑫<VC++深入詳解3.3.1>
3.3.1??三者之間關系
很多開發(fā)人員都將窗口類、窗口類的對象和窗口之間的關系弄混淆了。為了使讀者能更好地理解它們之間的關系,下面我們將模擬CWnd類的封裝過程。首先新建一個Win32?Application類型的工程,取名為“WinMain”。在隨后的向導窗口中選擇創(chuàng)建一個空工程(即選擇an empty project選項)。接著為該工程新建一個源文件WinMain.cpp。在該文件中,首先新建一個類CWnd,然后為其定義創(chuàng)建窗口函數(CreateEx)、顯示窗口函數(ShowWindow)和更新窗口函數(UpdateWindow)三個函數,并定義一個成員變量(m_hWnd)。具體代碼如例3-18所示。
例3-18
class CWnd
{
public:
??? BOOL CreateEx(DWORD dwExStyle,????? // extended window style
??????????????? LPCTSTR lpClassName,? // registered class name
??????????????? LPCTSTR lpWindowName, // window name
??????????????? DWORD dwStyle,??????? // window style
??????????????? int x,??????????????? // horizontal position of window
??????????????? int y,??????????????? // vertical position of window
??????????????? int nWidth,?????????? // window width
??????????????? int nHeight,????????? // window height
??????????????? HWND hWndParent,????? // handle to parent or owner window
??????????????? HMENU hMenu,????????? // menu handle or child identifier
??????????????? HINSTANCE hInstance,? // handle to application instance
??????????????? LPVOID lpParam);??????? // window-creation data
??? BOOL ShowWindow(int nCmdShow);
??? BOOL UpdateWindow();
public:
??? HWND m_hWnd;
};
?
小技巧:這些函數的參數可以參照MSDN中相應MFC函數的定義,然后直接復制這些參數即可。
?
提示:因為SDK函數數量很多,程序員記憶負擔很重。MFC中使用的大部分函數名與相應的SDK函數名相同,這樣做的目的就是為了方便程序員,減輕記憶負擔。程序員只需要記憶兩者中的一個就可以了。
接下來完成這三個函數的定義,代碼如例3-19所示。
例3-19
BOOL CWnd::CreateEx(DWORD dwExStyle,????? // extended window style
??????????????? LPCTSTR lpClassName,? // registered class name
??????????????? LPCTSTR lpWindowName, // window name
??????????????? DWORD dwStyle,??????? // window style
??????????????? int x,??????????????? // horizontal position of window
??????????????? int y,??????????????? // vertical position of window
??????????????? int nWidth,?????????? // window width
??????????????? int nHeight,????????? // window height
??????????????? HWND hWndParent,????? // handle to parent or owner window
??????????????? HMENU hMenu,????????? // menu handle or child identifier
??????????????? HINSTANCE hInstance,? // handle to application instance
??????????????? LPVOID lpParam)??????? // window-creation data
{
??? m_hWnd=::CreateWindowEx(dwExStyle,lpClassName,dwStyle,x,y,
??????????????????? nWidth,nHeight,hWndParent,hMenu,hInstance,
??????????????????? lpParam);
??? if(m_hWnd!=NULL)
??????? return TRUE;
??? else
??????? return FALSE;
}
?
BOOL CWnd::ShowWindow(int nCmdShow)
{
??? return ::ShowWindow(m_hWnd,nCmdShow);
}
?
BOOL CWnd::UpdateWindow()
{
??? return ::UpdateWindow(m_hWnd);
}
其中,我們定義的CWnd類的CreateEx函數需要完成創(chuàng)建窗口的工作,這可以利用Win32提供的SDK函數:CreateWindowEx函數來實現。該函數返回一個句柄,標識它所創(chuàng)建的窗口。這里,我們就可以利用已定義的CWnd類的成員變量m_hWnd來保存這個窗口句柄。因為我們定義的CreateEx函數返回值是個BOOL型,所以應該判斷一下這個窗口句柄。根據其值是否為空來決定函數是返回TRUE值,還是FALSE值。
讀者應注意的是,在實際開發(fā)時,應該初始化m_hWnd變量,這可以在構造函數中實現,給它賦一個初值NULL。這里我們只是為了演示CWnd類是如何與窗口關聯(lián)起來的,因此就不進行初始化工作了。
接下來定義ShowWindow函數的實現。同樣,需要調用Platform SDK函數,即ShowWindow來完成窗口的顯示。為了區(qū)分這兩個同名函數,在調用這個Platform SDK函數時,前面加上作用域標識符(即::)。這種以“::”開始的表示方法表明該函數是一個全局函數,這里表示調用的ShowWindow函數是Platform SDK函數。因為CreateEx函數已經獲取了窗口句柄并保存到m_hWnd成員變量中,所以,ShowWindow函數可以直接把這個句柄變量作為參數來使用。
提示:讀者在定義自己的成員函數時,如果調用的API函數名與自己的函數名不同,那么該API函數名前可以加也可以不加“::”符號,編譯器會自動識別API函數。但是如果當前定義的成員函數與內部調用的API函數名相同,那么后者前面必須加“::”符號,否則程序在編譯或運行時就會出錯。
我們自己定義的UpdateWindow函數的實現比較簡單,直接調用SDK函數:UpdateWindow完成更新窗口的工作。
從例3-19所示代碼可知,我們定義的CWnd類的后兩個函數(ShowWindow和UpdateWindow)內部都需要一個窗口句柄,即需要知道對哪個窗口進行操作。
現在我們就實現了一個窗口類:CWnd。但我們知道如果要以類的方式來完成窗口的創(chuàng)建、顯示和更新操作,那么首先還需要編寫一個WinMain函數。讀者并不需要記憶這個函數的寫法,只要機器上有MSDN就可以了,在MSDN中找到該函數的幫助文檔,直接復制其定義即可。這里,我們只是想講解在這個函數內部所做的工作,并不是真正的實現,因此只是寫出其主要的代碼,如例3-20所示。
例3-20
int WINAPI WinMain(
? HINSTANCE hInstance,????? // handle to current instance
? HINSTANCE hPrevInstance,? // handle to previous instance
? LPSTR lpCmdLine,????????? // command line
? int nCmdShow????????????? // show state
)
{
??? //首先是設計窗口類,即定義一個WNDCLASS,并為相應字段賦值。
??? WNDCLASS wndcls;
??? wndcls.cbClsExtra=0;
??? wndcls.cbWndExtra=0;
??? ......
//注冊窗口類
RegisterClass(&wndcls);
?
//創(chuàng)建窗口
CWnd wnd;
wnd.CreateEx(...);
?
//顯示窗口
wnd.ShowWindow(SW_SHOWNORMAL);
???
//更新窗口
wnd.UpdateWindow();
//接下來就是消息循環(huán),此處省略
......
return 0;
}
請讀者回想一下第1章中我們利用SDK編程時為創(chuàng)建窗口、顯示窗口和更新窗口所編寫的代碼(如例3-21所示),并比較例3-20和例3-21這兩段代碼的區(qū)別。
例3-21
??? HWND hwnd;
??? hwnd=CreateWindowEx();
??? ::ShowWindow(hwnd,SW_SHOWNORMAL);
??? ::UpdateWindow(hwnd);
我們可以發(fā)現,SDK程序中多了一個HWND類型的變量hwnd。該變量用來保存由CreateWindowEx函數創(chuàng)建的窗口句柄,并將其作為參數傳遞給隨后的顯示窗口操作(ShowWindow函數)和更新窗口操作(UpdateWindow函數)。而我們自定義的實現代碼中,CWnd類定義了一個HWND類型的成員變量:m_hWnd,用于保存這個窗口句柄。首先CWnd類的CreateEx函數創(chuàng)建窗口,并將該窗口句柄保存到這個成員變量,接著調用CWnd類的ShowWindow函數顯示窗口時,就不需要再傳遞這個句柄了,因為它已經是成員變量,該函數可以直接使用它。CWnd類的UpdateWindow函數也是一樣的道理。
許多程序員在進行MFC程序開發(fā)時,容易混淆一點:認為這里的CWnd類型的wnd這個C++對象所代表的就是一個窗口。因為在實踐中,他們看到的現象是:當C++窗口類對象銷毀時,相應的窗口也就沒了。有時正好巧合,當窗口銷毀時,C++窗口類對象的生命周期也到了,從而也銷毀了。正因為如此,許多程序員感覺C++窗口類對象就是窗口,窗口就是這個C++窗口類對象。事實并非如此。讀者可以想像一下,如果我們關閉了一個窗口,這個窗口就銷毀了,那么該窗口對應的C++窗口類對象銷毀了沒有呢?當然沒有。當一個窗口銷毀時,它會調用CWnd類的DestroyWindow函數,該函數銷毀窗口后,將CWnd成員變量:m_hWnd設為NULL。
C++窗口類對象的生命周期和窗口的生命周期不是一致的。當一個窗口銷毀時,與C++窗口類對象沒有關系,它們之間的紐帶僅僅在于這個C++窗口類內部的成員變量:m_hWnd,該變量保存了與這個C++窗口類對象相關的那個窗口的句柄。
另一方面,當我們設計的這個C++窗口類對象銷毀的時候,與之相關的窗口是應該銷毀的,因為它們之間的紐帶(m_hWnd)已經斷了。另外,窗口也是一種資源,它也占據內存。這樣,在C++窗口類對象析構時,也需要回收相關的窗口資源,即銷毀這個窗口。
因此,讀者一定要注意:C++窗口類對象與窗口并不是一回事,它們之間惟一的關系是C++窗口類對象內部定義了一個窗口句柄變量,保存了與這個C++窗口類對象相關的那個窗口的句柄。窗口銷毀時,與之對應的C++窗口類對象銷毀與否,要看其生命周期是否結束。但C++窗口類對象銷毀時,與之相關的窗口也將銷毀。在我們定義的這個WinMain程序(例3-20所示代碼)中,當程序運行到WinMain函數的右大括號(})時,該函數內部定義的Wnd窗口類對象的生命周期也就結束了。
這是我們自已定義的CWnd類,那么MFC提供的CWnd類是不是這樣實現的呢?讀者在MSDN中查看MFC提供的CWnd類,將會發(fā)現該類確實定義了一個數據成員:m_hwnd,用來保存與之相關的窗口的句柄。因為MFC中所有的窗口類都是由CWnd類派生的,于是,所有的窗口類(包括子類)內部都有這樣的一個成員用來保存與之相關的窗口句柄。所以,讀者不能認為我們前面創(chuàng)建的MFC程序Test中的CMainFrame類和CTestView類的對象就是一個窗口。
總結
以上是生活随笔為你收集整理的窗口类、窗口类对象与窗口 三者之间关系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GCC for Win32开发环境介绍
- 下一篇: VS2010/MFC编程入门之四(MFC