戏说 Windows GDI (3)
繼續討論之前的問題,包括GDI對象的刪除問題,窗口注冊與銷毀問題,最后用標尺的小例子說明問題
1.GDI對象的刪除
由CGDIObject派生類創建的畫筆、畫刷和其他對象都要占用內存資源,因此在使用完畢之后我們必須要刪除他們。當然,如果我們在棧上創建CPen、CBrush、CFont或其他CGDIObject,那么在CGDIObject超出范圍時,相關的GDI對象就會自動地刪除。如果用new在堆上創建了一個CGDIObject,那么我們就必須要手動的進行銷毀,以便調用它的析構函數。可以多了解一下,與CGDIObject相關聯的GDI對象可以通過調用CGDIObject::DeleteObject被顯示地消除。
在之前的16位Windows中,GDI對象經常會引發資源泄漏的問題,這時候因為某些程序不能刪除他們創建的GDI對象,因此Program Manager報告的可用系統資源的數量會隨著應用程序的不斷打開與終止漸漸減少。但是,在32位程序中,這個問題有所改善,Windows會跟蹤所分配的資源,并在程序結束的時候釋放他們。如果大量的畫筆畫刷沒有被釋放,他們就會占據Windows GDI的系統內存空間。很快,創建的畫筆畫刷的調用就會失敗,應用程序的OnPaint處理程序也會莫名其妙的停止工作。
刪除由用戶創建的GDI對象是重要的,而不能刪除已經選入設備描述表的GDI對象也同樣不容忽視。試圖用已刪除的對象畫圖的程序代碼是錯誤的。
下面的示例程序允許刪除選入設備的畫刷,我們可以分析一下其中的原因:
void CMainWindow::OnPain(){ CpaintDC dc(this); CBrush brush(RGB(255,0,0)); dc.SelectObject(&brush); dc.Ellipse(0,0,200,100); }事情是這樣的,CPaintDC對象和CBrush對象都是在棧上創建的。由于CBrush是第二個被創建的,所以特的析構函數會首先被調用,相關的GDI畫刷在dc超出范圍之前就被刪除了。但是要強調一點,如果代碼的健壯性依賴于棧變量的特定順序來創建,那么你的程序就是相當糟糕的。就代碼的維護性而言,這種程序簡直太可怕了!
最好的解決辦法就是:在CPaintDC對象超出范圍之前將CBrush從設備描述表中提出。示例如下:
CPen pen ( PS_SOLID,1,RGB(255,0,0)); CPen* pOldPen = dc.SelectObject( &pen); CBrush brush(RGB(0,0,255)); CBrush* pOldBrush = dc.SelectObject( &brush); .... dc.SelectObject(pOldPen); dc.SelectObject(pOldBrush);
2.窗口是如何消除的
我們研究的MFC程序很多都是在InitInstance中用下面的語句創建了一個窗口:
m_pMainWnd = new CMainWindow;應該注意到,這個對象也是用new創建的,所以InitInstance結束之后,對象應該還存在內存之中。實際上,知道delete語句執行后,對象才會被清除。然而,在源程序中,我們似乎沒有發現這條語句。這是因為MFC類庫可以自己刪除對象。更確切地說就是對象自己刪除了自己。這個技巧的關鍵在于:窗口在消除之前接收到的最后一條消息是WM_NCDESTROY。在源代碼的CWnd::OnNcDestroy中,我們會發現,程序里面調用了一個虛函數PostNcDestroy.CFrameWnd覆蓋PostNcDestroy并執行了語句:
delete this;因此在框架窗口被清除時,與窗口有關的對象也自動被刪除了。框架窗口在用戶關閉應用程序時被清除。
這里必須值得注意的是:CWnd自己實現的PostNcDestroy并沒有刪除相關的窗口對象,因此,如果我們程序中的窗口是直接從CWnd中派生而來的,我們必須對虛函數PostNcDestroy進行覆蓋,也就是添加delete this語句。
3.標尺應用程序
效果圖:
源代碼:
CRuler.h
class CMyApp : public CWinApp { public:virtual BOOL InitInstance (); };class CMainWindow : public CFrameWnd { public:CMainWindow ();protected:afx_msg void OnPaint ();DECLARE_MESSAGE_MAP () };
CRuler.c
#include <afxwin.h> #include "Ruler.h"CMyApp myApp;/ // CMyApp member functionsBOOL CMyApp::InitInstance () {m_pMainWnd = new CMainWindow;m_pMainWnd->ShowWindow (m_nCmdShow);m_pMainWnd->UpdateWindow ();return TRUE; }/ // CMainWindow message map and member functionsBEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)ON_WM_PAINT () END_MESSAGE_MAP ()CMainWindow::CMainWindow () {Create (NULL, _T ("Ruler")); }void CMainWindow::OnPaint () {CPaintDC dc (this);//// Initialize the device context.//dc.SetMapMode (MM_LOENGLISH);dc.SetTextAlign (TA_CENTER | TA_BOTTOM);dc.SetBkMode (TRANSPARENT);//// Draw the body of the ruler.//CBrush brush (RGB (255, 255, 0));CBrush* pOldBrush = dc.SelectObject (&brush);dc.Rectangle (100, -100, 1300, -200);dc.SelectObject (pOldBrush);//// Draw the tick marks and labels.//for (int i=125; i<1300; i+=25) {dc.MoveTo (i, -192);dc.LineTo (i, -200);}for (int i=150; i<1300; i+=50) {dc.MoveTo (i, -184);dc.LineTo (i, -200);}for (int i=200; i<1300; i+=100) {dc.MoveTo (i, -175);dc.LineTo (i, -200);CString string;string.Format (_T ("%d"), (i / 100) - 1);dc.TextOut (i, -175, string);} } 與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的戏说 Windows GDI (3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++中的近指令、远指针和巨指针
- 下一篇: 飞鸽传书传文件