网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
(2)ON_WM_LBUTTONDOWN消息映射宏在CDrawView类的源文件(DrawView.cpp)中,有下所示代码。
BEGIN_MESSAGE_MAP(CDrawView, CView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
上述例5-4所示代码中,BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()这两个宏之间定义了CDrawView类的消息映射表,其中有一个ON_WM_LBUTTONDOWN消息映射宏,这个宏的作用就是把鼠标左键按下消息(WM_LBUTTONDOWN)与一个消息响应函数关联起来(在本例中是把WM_LBUTTONDOWN消息与OnLButtonDown函数关联起来)。通过这种机制,一旦有消息产生,程序就会调用相应的消息响应函数来进行处理。
(3)消息响应函数的定义在CDrawView类的源文件(DrawView.cpp)中,可以看到OnLButtonDown函数的定义。
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
MessageBox(L"view clicked");
CView::OnLButtonDown(nFlags, point);
}
经过以上分析可以知道,一个 MFC 消息响应函数在程序中有三处相关信息:函数原型、函数实现,以及用来关联消息和消息响应函数的宏。在头文件中使用afx_msg宏来表明这是一个消息响应函数原型的声明,在源文件中有两处:一处是在**BEGIN_MESSAGE_MAP(…)**和 **END_MESSAGE_MAP()**这两个宏之间的消息映射宏 ON_WM_LBUTTOND OWN();另一处是源文件中的消息响应函数的实现代码。
在讲述消息循环时曾介绍过,当有消息产生时,操作系统会把这条消息放到应用程序的消息队列中,应用程序通过 GetMessage 函数从这个队列中取出一条具体的消息,并通过 DispatchMessage 函数把消息交给操作系统,后者调用应用程序的窗口过程,即窗口过程函数 WndProc 进行处理。该函数利用 switch-case 结构来对消息进行判别并分类处理。然而,我们看到在 MFC 程序中,并不是按照这种途径进行处理的,只要遵照上述步骤,定义了与消息有关的三处信息后,就可以实现消息的响应处理。MFC中采用的这种消息处理机制称为MFC消息映射机制。
实际上,消息路由可以有多种实现方式。其中一种可能的实现方式是,在基类中针对每种消息定义一个虚函数。当子类需要对某个消息进行处理时,只需要重写基类相应的虚函数即可。当在程序中调用这个函数时,根据多态性原理,如果子类重写了该函数,就调用子类的这个函数,否则就调用父类的相应函数。通过这种方式也可以完成消息的路由。这样做从原理上讲是没有问题的,但是从编程角度讲,是不可取的。
为什么不可取?因为虚函数必须由一个虚函数表(vtable)来实现。在应用程序中使用的每个派生类,系统都要为它们分配一个 vtable,并且不管基类中虚函数是否在派生类中被重写,这个vtable表都要为基类的每一个虚函数提供一个4字节的输入项。而我们知道,MFC中类的派生层次有很多,这样做将使MFC类及其派生类背着一个很大的虚拟函数表的包袱,这对内存资源是一种浪费。而且,每次扫描虚拟函数表也非常消耗时间,这种定义虚拟函数的做法显然是不合适的。所以,MFC没有采用虚拟函数这种机制,而是采用一种称之为消息映射的机制来完成消息路由。
MFC 消息映射机制的具体实现方法是:在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类能否处理此消息。如果能处理该消息,那么依照静态表同样能很容易找到并调用对应的消息处理函数。
下面让我们看看MFC消息映射机制的实际实现过程。MFC在后台维护了一个窗口句柄与对应的C++对象指针的对照表。以本例中的CDrawView类为例,与CDrawView对象相关的有一个窗口,窗口当然有它的窗口句柄,该句柄与CDrawView对象的一个指针(即CDrawView*)存在着一一对应关系,在窗口句柄与 C++对象对照表中就维护了这种对应关系。当收到某一消息时,消息的第一个参数就指明该消息与哪个窗口句柄相关,通过对照表,就可以找到与之相关的C++对象指针。把这个指针传递给应用程序框架窗口类的基类,后者会调用一个名为WindowProc的函数。该函数的定义位于wincore.cpp文件,代码如下所示。
WindowProc的函数(wincore.cpp)
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult))
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
转到WindowProc函数的声明处,我们可以发现它是一个虚函数。在这个函数的内部调用了一个 OnWndMsg 函数,真正的消息路由,也就是消息映射就是由此函数完成的。OnWndMsg函数的定义也位于wincore.cpp文件中,部分代码如下所示。
if (!OnWndMsg(message, wParam, lParam, &lResult))
OnWndMsg 函数(wincore.cpp**)**
连接:VC++ OnWndMsg 函数(wincore.cpp)
OnWndMsg函数的处理过程是:
判断消息是否有消息响应函数。
判断方法是在消息映射表中进行查找。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**