c++虚函数调用,通过一个构造函数调用虚函数时,c++系统对该调用采用
1.创造
2.虚函数
3.预分类窗口
4.预传输消息
5.窗口进程
6.OnCommand
7.通知
8.OnChildNotify
9.执行内定的消息处理
10.销毁窗口
11.销毁后
拥塞窗口作为MS-VisualC++的类库(微软基础班的缩写)中最基本的与窗口打交道的类,完成了大部分窗口管理任务。同时提供了很多虚拟函数,这些虚拟函数在适当的地方提供了供派生类参与管理的接口。
一直以来,对这些虚拟函数的来龙去脉有所糊涂,无法明确的判断他们在什么时候调用,又缺省完成了些什么。重载时哪些是要注意的.等等。
抽时间查看了MS-VisualC++的类库(微软基础班的缩写)的原码,想看其究竟。
总结如下:
1.创造
虚拟BOOL Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT rect,CWnd* pParentWnd,UINT nID,ccreate context * p context=NULL);
调用时机:
窗口建立时
作为主窗口,大多在初始实例()中将直接或间接调用创造
作为子窗口,大多再父窗口建立后发出WM_CREATE消息,对其进行处理时OnCreate()中调用。
功能:
控制建立细节
拥塞窗口实现:
.
//注册窗口类,调用应用程序接口建立窗口
//允许修改几个常见的创建参数
创建结构化cs;
cs。dwex style=dwex style
cs。lpsz class=lpsz类名;
cs.lpszName=lpszWindowName
cs.style=dwStyle
cs.x=x
cs.y=y
cs.cx=nWidth
cs.cy=nHeight
cs。hwnd parent=hwnd parent
cs。hm enu=nIDorHMenu
cs。h instance=AfxGetInstanceHandle();
cs.lpCreateParams=lpParam
//在此调用虚拟函数预创建窗口,允许在实际建立之前"篡改"建立参数。
如果(!预创建窗口(计算机科学)
{
post NC destroy();
返回错误的
}
AfxHookWindowCreate(this);
HWND HWND=:CreateWindowEx(cs。dwex样式,cs.lpszClass,
cs.lpszName,cs.style,cs.x,cs.y,cs.cx,cs.cy,
cs.hwndParent,cs.hMenu,cs.hInstance,cs。lpcreateparams);
#ifdef _DEBUG
if (hWnd==NULL)
{
TRACE1(警告:窗口创建失败:GetLastError返回0x%8.8X/n ,
GetLastError());
}
#endif
如果(!AfxUnhookWindowCreate())
post NC destroy();//如果CreateWindowEx过早失败,则清除
if (hWnd==NULL)
返回错误的
ASSERT(hWnd==m _ hWnd);//应该已经在发送消息挂钩中设置
返回真实的
}
2.虚函数
调用时机:
参见上段,在创建()中,设置好窗口建立数据铯后,在实际建立窗口之前,将cs "暴露"给派生类,允许派生类在此时改变窗口建立参数。
功能:
控制建立参数(在创建()中可以设置建立信息,但创造有时是框架结构隐含调用的,故在虚函数时,再提供一个修订窗口建立参数的机会)。
拥塞窗口实现:
BOOL CWnd:预创建窗口(创建结构cs)
{
//如果在派生类中用户没有定制类名,没有制定窗口类名,使用MS-VisualC++的类库(微软基础班的缩写)默认注册类
if (cs.lpszClass==NULL)
{
//确保默认窗口类已注册
VERIFY(AfxDeferRegisterClass(AFX _ WND _ REG));
//未提供窗口类结构使用子窗口默认值
断言(cs。样式WS _ CHILD);
cs。lpsz class=_ afx wnd
}
返回真实的
}
如果需要,使用自定的窗口类,应该在派生类的虚函数中注册,并得到并指定类名。
3.预分类窗口
调用时机:
建立窗口的同时将C Wnd对象附着在窗口上
CWnd:Create()中:
.
AfxHookWindowCreate(this);
HWND HWND=:CreateWindowEx(cs。dwex样式,cs.lpszClass,
cs.lpszName,cs.style,cs.x,cs.y,cs.cx,cs.cy,
cs.hwndParent,cs.hMenu,cs.hInstance,cs。lpcreateparams);
AfxUnhookWindowCreate();
.
建立窗口时,系统建立WH CBT(训练)钩子(截获窗口动作),在钩子函数中完成拥塞窗口对象对窗口的包裹。
文件://操作很多,主要有
//pWndInit为传入的参数,应该就是拥塞窗口对象指针了
//对象连接到窗口句柄
pWndInit-Attach(hWnd);
.
//调用虚拟函数课前窗口,给用户一个定义相关操作的机会,例如,子控件的附着
pwnd init-PreSubclassWindow();
.
//设置消息处理函数等等。
WNDPROC * pOldWndProc=pwn dinit-GetSuperWndProcAddr();
.
WNDPROC afxWndProc=AfxGetAfxWndProc();
oldWndProc=(WNDPROC)SetWindowLong(hWnd,GWL_WNDPROC
(DWORD)afxWndProc);
.
拥塞窗口实现:
拥塞窗口类中,在此虚拟函数中没有缺省动作。
4.预传输消息
调用时机:
进程的消息队列处理循环中,在将窗口的消息分发到窗口的消息处理函数之前将调用对象虚拟函数PreTranslateMessage,允许再窗口派生类中对即将发送的消息进行处理。
而CWinApp:PreTranslateMessage将有可能调用到窗口的预翻译消息。
先来看以下CWinThread:PumpMessage中对消息的分发过程
.
* GetMessage(m _ msg cur,NULL,NULL,NULL)
.
//CWinThread的预传输消息虚拟函数被调用
if (m_msgCur.message!=WM_KICKIDLE!PreTranslateMessage( m_msgCur))
{
翻译消息(m _ msg cur);
*调度消息(m _ msg cur);
}
BOOL CWinThread:PreTranslateMessage(MSG * pMsg)
{
.
CWnd * pMainWnd=AfxGetMainWnd();
//依此调用从命令发出窗口到主窗口间各级窗口的PreTranslateMessage();
//参见下面的WalkPreTranslateTree原码
if(CWnd:WalkPreTranslateTree(pMainWnd-GetSafeHwnd()、pMsg))
返回真实的
//在非模态对话框的情况下,最后一次机会通过主要的
//窗口的快捷键表
if (pMainWnd!=空)
{
CWnd * pWnd=CWnd:from句柄(pMsg-hwnd);
if(pWnd-gettoplevelparant()!=pMainWnd)
返回pMainWnd-PreTranslateMessage(pMsg);
}
返回FALSE//没有特殊处理
}
BOOL PASCAL CWnd:WalkPreTranslateTree(HWND hWndStop,MSG* pMsg)
{
.
//依次调用各级窗口的预传输消息
for(HWND HWND=pMsg-HWND;hWnd!=NULLhWnd=:GetParent(hWnd))
{
CWnd * pWnd=CWnd:fromnhandlepermanent(hWnd);
如果(pWnd!=空)
{
if(pWnd-PreTranslateMessage(pMsg))
返回TRUE//被目标窗口捕获(例如:加速器)
}
//在没有兴趣的情况下到达hWndStop窗口
if (hWnd==hWndStop)
打破;
}
返回FALSE//没有特殊处理
}
5.窗口进程
调用时机:
窗口建立后,将进入消息循环。在此期间,WindowPro被调用以处理各消息。
在窗口建立时,消息处理函数被制定,一般是AfxWndProc,其将调用AfxCallWndProc,而AfxCallWndProc最终将调用到虚拟函数WindowProc。
功能:
允许派生类在消息处理前,添加处理。
拥塞窗口实现:
LRESULT CWnd:window proc(UINT message,WPARAM wParam,LPARAM lParam)
{
LRESULT LRESULT=0;
文件://主要是由OnWndMsg完成消息的分类,分解处理。
如果(!OnWndMsg(消息,wParam,lParam,lResult))
文件://剩余部分交由缺省命令处理函数处理。
lResult=DefWindowProc(message,wParam,lParam);
返回lResult
}
附:OnWndMsg流程
在OnWndMsg中,将根据消息的性质,归类成命令消息、通知消息、普通消息
分别由OnCommand、OnNotify.处理
BOOL CWnd:OnWndMsg(UINT message,WPARAM wParam,LPARAM lParam,LRESULT* pResult)
{
//如果是WM _命令消息,由虚拟函数OnCommand处理,
//WM_COMMAND由菜单、工具条等发出,表示特定的命令,与窗口消息由所不同。
if (message==WM_COMMAND)
{
.
OnCommand(wParam,lParam))
.
}
//如果消息是WM _通知,即通知消息,由虚拟函数通知处理,
if (message==WM_NOTIFY)
{
.
OnNotify(wParam,lParam,lResult))
.
}
//对特殊消息的处理:
WM_ACTIVATE.
WM _游标.
//普通消息
.
//在类消息映射中查找消息对应的消息处理函数。
//参数转换等等.
.
//找到后,调用该函数。
mmf.pfn=lpEntry-
lResult=(这-* MMF。pfn _ lwl)(wParam,lParam);
.
}
6.OnCommand
调用时机:
在OnWndMsg中,如果消息是WM_COMMAND,即命令消息,将调用OnCommand
在OnCommand中可以对命令处理进行操作。
拥塞窗口实现:文件://参见对命令更新机制的分析
BOOL CWnd:on命令(WPARAM WPARAM,LPARAM lParam)
{
.
//试探性的调用OnCmdMsg(,CN_UPDATE_COMMAND_UI.)看当前命令项是否有效
CTestCmdUI状态;
国家。m _ nID=nID
oncmdmmsg(nID,CN_UPDATE_COMMAND_UI,state,NULL);
如果(.)//设置标志为命令有效时的命令标志:CN_COMMAND
nCode=CN _ COMMAND
.
//如果是子窗口的通知消息,反映到子窗口?如果子窗口有相应的处理,就会返回。如果不是,它仍被视为命令。
//有些通知消息是通过WM_COMMAND发送的
if(ReflectLastMsg(hWndCtrl))
返回TRUE//被孩子吃了
//命令消息排序后,调用虚函数OnCmdMsg!
返回oncmdmmsg(nID,nCode,NULL,NULL);
}
关于命令处理的具体过程,请参考文章中的相关文章。
7.奥诺诺蒂
BOOL CWnd:OnNotify(WPARAM,LPARAM lParam,LRESULT* pResult)
通话时间:
在wndmsg中,如果要处理的消息是WM_NOTIFY,则会调用OnNotify来具体处理通知消息。
虚拟OnNotify函数为管理派生类中的通知消息提供了一个接口。
CWnd实现:boolcwnd: Onnotify (WParam,LPARAM LPARAM,LRESULT * PRESULT) {.file://将通知消息反映给发送通知的窗口,由窗口处理。如果窗口没有被处理,它将被OnCmdMSg处理。if (ReflectLastMsg(hWndCtrl,pResult))返回TRUE//孩子吃.file://留给OnCmdMsg。对应的消息映射宏为returnoncmdmmsg(NID,makelong (ncode,WM _ notify),ify,null);}
关于消息反射:
控件通常以通知消息的形式将其自身的更改通知给父窗口。由父窗口响应处理。
MFC的反射机制可以将通知消息发送回原窗口,事件可以在原窗口的消息映射系统中处理。这有助于窗口函数的封装。
如果子窗口没有对通知的反射处理功能,则通知消息仍由父窗口处理。
在ReflectLastMsg中,将调用pwnd-sendchilnotifylastmsg(pwnd是指向子窗口的指针)。
将在SendChildNotifyLastMsg中调用虚函数OnChildNotify。
在子窗口的OnChildNotify中,可以在接收反射消息之前添加处理。
8.OnChildNotify
BOOL CWnd:OnChildNotify(UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT* pResult)
通话时间:
通知窗口的父窗口后,反射回来时会先调用OnChildNotify。
您可以在此功能中监控和处理来自父窗口的通知消息。
CWnd的实施:
调用CWnd成员ReflectChildNotify。
实际上,消息处理仍然由原来的消息和命令处理流程来完成。区别在于它们的消息和命令值被调整以区分它们自己的消息和命令。
在类的消息映射项中,反射消息处理宏完成相应通知消息与处理函数的关联。
BOOL CWnd:reflect child notify(UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT* pResult)
{
//将返回的消息分类处理成特定的消息格式。
开关(uMsg)
{
//普通消息
//WM_HSCROLL,WM_VSCROLL:
.
//转换为反射的消息号,由命令处理。(在消息映射中,反射消息的序列号是WM_REFLECT_BASE uMsg)
return CWnd:OnWndMsg(WM _ REFLECT _ BASE uMsg,wParam,lParam,pResult);
//如果是WM_COMMAND
案例WM_COMMAND:
{
.
//直接给窗口的OnCmdMsg。同时命令的序号也相应改变,与窗口本身接收到的同一命令不同,有不同的消息映射项。
CWnd:oncmdmmsg(0,MAKELONG(nCode,WM_REFLECT_BASE WM_COMMAND),NULL,NULL))
.
}
//如果是WM_NOTIFY通知
案例WM_NOTIFY:
{
//.
//留给OnCmdMsg处理。更改命令序列号。
CWnd:oncmdmmsg(0,MAKELONG(nCode,WM_REFLECT_BASE WM_NOTIFY),ify,NULL);
.
}
//颜色类别
if(uMsg=WM _ CTLCOLORMSGBOX uMsg=WM _ CTLCOLORSTATIC)
{
.
CWnd:OnWndMsg(WM _ REFLECT _ BASE WM _ CTL color,0,(LPARAM) ctl,pResult);
.
}
.
}
9.执行内定的消息处理
通话时间:
WindowProc中的消息经过OnWndMsg后,没有找到对应的处理函数,会被DefWindowProc处理。
在DefWindowProc中,可以给这些未处理的消息添加相应的操作。
CWnd实施
LRESULT CWnd:DefWindowProc(UINT nMsg,WPARAM wParam,LPARAM lParam)
{
if (m_pfnSuper!=空)
return:CallWindowProc(m _ pfn super,m_hWnd,nMsg,wParam,lParam);
WNDPROC pfnWndProc
if((pfnWndProc=* GetSuperWndProcAddr())==NULL)
return :DefWindowProc(m_hWnd,nMsg,wParam,lParam);
其他
return:CallWindowProc(pfnWndProc,m_hWnd,nMsg,wParam,lParam);
}
10.销毁窗口
我们先来看看CWnd对DestroyWindow的实现。
BOOL CWnd:DestroyWindow()
{
.
//破坏窗口
if (m_pCtrlSite==NULL)
b result=:destroy window(m _ hWnd);
其他
b result=m _ pCtrlSite-destroy control();
.
//C窗口对象从窗口中分离
detach();
}
在这个实现中,调用API: bool销毁窗口(hwnd hwnd);
API的DestroyWindow将向窗口发送WM_DESTROY和WM_NCDESTROY消息,
通话时间:
对于主窗口:(CFrameWnd)
当窗口收到关闭消息时,它将调用DestroyWindow。
关闭邮件发送:
在菜单上选择exit会给出一个ID_APP_EXIT命令,默认在CWinApp:OnAppExit中实现:发送WM_CLOSE到主窗口;
此外,按下窗口关闭按钮会给窗口一个WM_CLOSE消息。
在WM_CLOSE的缺省处理OnClose()中
void CFrameWnd:OnClose()
{
.
destroy window();
.
}
对于其他窗口
主窗口销毁时会调用:DestroyWindow,这个API会向窗口发送WM_DESTROY和WM_NCDESTROY消息。
并自动完成子窗口的销毁。
在MFC中,子窗口的DestroyWindow虚函数还没有被调用,但是可以重载,在需要的时候自己调用。控制子窗口的销毁。
11.销毁后
通话时间:
窗口销毁后,在WM_NCDESTROY的处理函数OnNcDestroy()中调用。
在PostNcDestroy中,一般会完成C的窗口对象删除等收尾工作。
CWnd的实施:
空的
CFrameWnd实现
删除这个;(删除窗口对象)
本文来自http://blog.csdn.net/FMD/archive/2001/06/16/5529.aspx. CSDN博客,转载请注明出处
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。