//========================================================================
//TITLE:
// 三论在C++类中实现Windows窗口的创建
//AUTHOR:
// norains
//DATE:
// Tuesday 16-January -2007
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
关于在C++类中创建windows窗口,之前已经在《在C++类中实现Windows窗口的创建》以及《对<在C++类中实现Windows窗口的创建>一文的补充》这两篇文章中讨论,不过其缺点也是显而易见的:前者对外暴露了一个丑陋的接口,没有真正意义上的完全封装;后者引入了友元机制,使得代码异常复杂,并且还有一个很大的隐患,就是如果定义多个对象,会造成只有最后一个对象实例正常运作.
而本文将要讨论的封装,则是改进以上缺点,令代码更加安全且通俗易懂.
我们以一个已经封装好的窗口类作为例子,首先请看源代码:
/**/
//
// DrawWnd.h: interface for the CDrawWnd class.
/**/ //
#ifndef DRAWWND_H
#define DRAWWND_H
class CDrawWnd
... {
public:
BOOL ShowWindow(BOOL bShow);
BOOL Initialize(HINSTANCE hInst);
static CDrawWnd * GetInstance();
virtual ~CDrawWnd();
protected:
static LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
void OnPaint(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
CDrawWnd();
static CDrawWnd *m_pInstance;
} ;
#endif // !defined DRAWWND_H
// DrawWnd.h: interface for the CDrawWnd class.
/**/ //
#ifndef DRAWWND_H
#define DRAWWND_H
class CDrawWnd
... {
public:
BOOL ShowWindow(BOOL bShow);
BOOL Initialize(HINSTANCE hInst);
static CDrawWnd * GetInstance();
virtual ~CDrawWnd();
protected:
static LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
void OnPaint(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
CDrawWnd();
static CDrawWnd *m_pInstance;
} ;
#endif // !defined DRAWWND_H
/**/
//
// DrawWnd.cpp: implementation of the CDrawWnd class.
/**/ //
#include " stdafx.h "
#include " DrawWnd.h "
// -------------------------------------------------------------------
// Intialize
CDrawWnd * CDrawWnd::m_pInstance = NULL;
// --------------------------------------------------------------------
// Macro define
#define DRAWWND_CLASS TEXT("DrawWnd_Class")
#define DRAWWND_TITLE TEXT("DrawWnd_Title")
// Screen width
#define SCREEN_WIDTH GetSystemMetrics(SM_CXSCREEN)
// Screen height
#define SCREEN_HEIGHT GetSystemMetrics(SM_CYSCREEN)
/**/ //
// Construction/Destruction
/**/ //
CDrawWnd::CDrawWnd()
... {
m_hInst = NULL;
m_hWnd = NULL;
}
CDrawWnd:: ~ CDrawWnd()
... {
delete m_pInstance;
m_pInstance = NULL
}
// ----------------------------------------------------------------------
// Description:
// Get the object instance
// --------------------------------------------------------------------
CDrawWnd * CDrawWnd::GetInstance()
... {
if(m_pInstance == NULL)
...{
m_pInstance = new CDrawWnd();
}
return m_pInstance;
}
// ----------------------------------------------------------------------
// Description:
// The window process
// --------------------------------------------------------------------
LRESULT CDrawWnd::WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
... {
switch(wMsg)
...{
case WM_MOUSEMOVE:
m_pInstance->OnPaint(hWnd,wMsg,wParam,lParam);
return 0;
}
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}
// ----------------------------------------------------------------------
// Description:
// Initialize the window
// --------------------------------------------------------------------
BOOL CDrawWnd::Initialize(HINSTANCE hInst)
... {
m_hInst = hInst;
WNDCLASS wc;
wc.style=0;
wc.lpfnWndProc=WndProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hInst;
wc.hIcon=NULL;
wc.hCursor=NULL;
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName=NULL;
wc.lpszClassName=DRAWWND_CLASS;
if(RegisterClass(&wc)==0)
...{
return FALSE;
}
m_hWnd = CreateWindow(
DRAWWND_CLASS,
DRAWWND_TITLE,
WS_POPUP ,
0,
0,
SCREEN_WIDTH,
SCREEN_HEIGHT,
NULL,
NULL,
m_hInst,
NULL
);
if(IsWindow(m_hWnd)==FALSE)
...{
return FALSE;
}
return TRUE;
}
// ----------------------------------------------------------------------
// Description:
// Show the window
// --------------------------------------------------------------------
BOOL CDrawWnd::ShowWindow(BOOL bShow)
... {
if(m_hWnd == NULL)
...{
return FALSE;
}
if(bShow == TRUE)
...{
::ShowWindow(m_hWnd,SW_SHOW);
}
else
...{
::ShowWindow(m_hWnd,SW_HIDE);
}
return TRUE;
}
// ----------------------------------------------------------------------
// Description:
// Show the window
// --------------------------------------------------------------------
void OnPaint(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
... {
DefWindowProc(hWnd,wMsg,wParam,lParam);
}
// DrawWnd.cpp: implementation of the CDrawWnd class.
/**/ //
#include " stdafx.h "
#include " DrawWnd.h "
// -------------------------------------------------------------------
// Intialize
CDrawWnd * CDrawWnd::m_pInstance = NULL;
// --------------------------------------------------------------------
// Macro define
#define DRAWWND_CLASS TEXT("DrawWnd_Class")
#define DRAWWND_TITLE TEXT("DrawWnd_Title")
// Screen width
#define SCREEN_WIDTH GetSystemMetrics(SM_CXSCREEN)
// Screen height
#define SCREEN_HEIGHT GetSystemMetrics(SM_CYSCREEN)
/**/ //
// Construction/Destruction
/**/ //
CDrawWnd::CDrawWnd()
... {
m_hInst = NULL;
m_hWnd = NULL;
}
CDrawWnd:: ~ CDrawWnd()
... {
delete m_pInstance;
m_pInstance = NULL
}
// ----------------------------------------------------------------------
// Description:
// Get the object instance
// --------------------------------------------------------------------
CDrawWnd * CDrawWnd::GetInstance()
... {
if(m_pInstance == NULL)
...{
m_pInstance = new CDrawWnd();
}
return m_pInstance;
}
// ----------------------------------------------------------------------
// Description:
// The window process
// --------------------------------------------------------------------
LRESULT CDrawWnd::WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
... {
switch(wMsg)
...{
case WM_MOUSEMOVE:
m_pInstance->OnPaint(hWnd,wMsg,wParam,lParam);
return 0;
}
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}
// ----------------------------------------------------------------------
// Description:
// Initialize the window
// --------------------------------------------------------------------
BOOL CDrawWnd::Initialize(HINSTANCE hInst)
... {
m_hInst = hInst;
WNDCLASS wc;
wc.style=0;
wc.lpfnWndProc=WndProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hInst;
wc.hIcon=NULL;
wc.hCursor=NULL;
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName=NULL;
wc.lpszClassName=DRAWWND_CLASS;
if(RegisterClass(&wc)==0)
...{
return FALSE;
}
m_hWnd = CreateWindow(
DRAWWND_CLASS,
DRAWWND_TITLE,
WS_POPUP ,
0,
0,
SCREEN_WIDTH,
SCREEN_HEIGHT,
NULL,
NULL,
m_hInst,
NULL
);
if(IsWindow(m_hWnd)==FALSE)
...{
return FALSE;
}
return TRUE;
}
// ----------------------------------------------------------------------
// Description:
// Show the window
// --------------------------------------------------------------------
BOOL CDrawWnd::ShowWindow(BOOL bShow)
... {
if(m_hWnd == NULL)
...{
return FALSE;
}
if(bShow == TRUE)
...{
::ShowWindow(m_hWnd,SW_SHOW);
}
else
...{
::ShowWindow(m_hWnd,SW_HIDE);
}
return TRUE;
}
// ----------------------------------------------------------------------
// Description:
// Show the window
// --------------------------------------------------------------------
void OnPaint(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
... {
DefWindowProc(hWnd,wMsg,wParam,lParam);
}
一些具体的设计细节请容许我稍后再谈,现在就让我们来看看怎么让这个类正常运作吧!
由于已经将构造函数声明为protected,所以我们只能申明一个对象指针.等等,也许有人会问,为什么要把构造函数声明为指针?恩,心急的家伙们,这个问题还是暂且搁下,留待稍后再论,现在我们主要任务是还是前面所说的:让类正常运作!
//
声明一个对象指针:
CDrawWnd * pDrawWnd;
// 获取一个对象实例:
pDrawWnd = CDrawWnd::GetInstance();
// 初始化窗口:
if (pDrawWnd -> Initialize(hInstance) == FALSE)
... {
return 0x05;
}
// 如果窗口初始化成功,我们就可以让它显示了
if (pDrawWnd -> ShowWindow(TRUE) == FALSE)
... {
return 0x10;
}
CDrawWnd * pDrawWnd;
// 获取一个对象实例:
pDrawWnd = CDrawWnd::GetInstance();
// 初始化窗口:
if (pDrawWnd -> Initialize(hInstance) == FALSE)
... {
return 0x05;
}
// 如果窗口初始化成功,我们就可以让它显示了
if (pDrawWnd -> ShowWindow(TRUE) == FALSE)
... {
return 0x10;
}
恩,对的,就是这么简单,四条语句,就可以让窗口正常显示在我们屏幕上.是不是觉得很轻松呢?恩哈,那就好,希望你还能保持这种愉快的心情的来继续讨论CDrawWnd类的实现细节.
看过我写的《对<在C++类中实现Windows窗口的创建>一文的补充》的朋友,对于现在的这个类应该非常高兴了,没有异常烦琐的友元,也没有声明多个对象导致产生的指针错误,一切都显得那么美好,难道不是么?
CDrawWnd类之所以能正常工作,关键在于声明了一个静态回调函数GetInstance()和一个同样也是静态的对象实例m_pInstance.
GetInstance()主要作用便是返回m_pInstance对象,让该类能起到相应的作用.m_pInstance在我们的类里,也是一个辛勤的员工,因为回调函数WndProc()里对消息的所有操作,都需要m_pInstance亲力而为.在我们上面的示例代码中,我们就可以看到这名辛勤的员工是如何帮助我们处理WM_PAINT消息的:
m_pInstance
->
OnPaint(hWnd,wMsg,wParam,lParam);
虽然最后OnPaint()函数最后也只是很无聊地调用DefWindowProc()完成它的使命,但这毕竟是m_pInstance架起的桥梁,不是么?
当然咯,m_pInstance勤劳归勤劳,但它也是有脾气的:静态的变量,不属于任何对象,只属于类.明白问题的所在了么?也就是说,如果我们定义多个对象,那么m_pInstance永远只指向最后一个定义的对象.嗯哈,由此引发的问题,估计就不是不严重了.
也正是因为这个原因,所以我们只能将功劳巨大的构造函数定义为protected.这样,我们就永远不能直接定义类的对象,也就完全杜绝上文提到的那个非常严重的结果.
现在,如果想要获取类的对象实例,我们就只能通过GetInstance().而在GetInstance()内部,我们当然不会坐以待毙,自然做了相应处理,让其永远只能定义一个类的对象实例.
好了,现在是不是已经豁然开朗,性情舒畅了呢?不过,先别急,再让我们看看一个非常有趣的问题.
如果我们是在EVC4.0或VC6.0下进行调试,试着把下面这条语句屏蔽掉然后再编译:
CDrawWnd
*
CDrawWnd::m_pInstance
=
NULL;
屏蔽掉了么?是不是很奇怪,编译居然无法通过,并且提示的错误是重复定义了m_pInstance!
我们再来看点更有趣的,尝试着把GetInstance()函数的实现从类外移回类的声明处.我们再编译一次,嗯,看到什么了么?顺利编译通过!
呵呵,C++标准中可没有规定返回静态变量的函数只能在类的声明处进行定义哦.所以嘛,接下来我要说的估计大家也都知道,就不再罗嗦了.