热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

分析MFC中的映射

分析MFC中的映射条件查找映射MFC中大量使用了BEGIN_XXX_MAP这样的宏,以及映射进行查找优化,例如消息映射,OLE命令映射&#

分析MFC中的映射

条件查找映射

MFC中大量使用了BEGIN_XXX_MAP这样的宏,以及映射进行查找优化,例如消息映射,OLE命令映射,以及接口等等。每个映射包含一个指向基类的映射的指针。这样,当一个类需要根据一定的条件查找一个对象时,它会查找本类对象,如果没有找到,那么会查找基类,直到根基类。这类查找包含Windows消息,命令,事件和OLE命令的分发,和对象实现的接口的查询等等。

下面是函数BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)的部分代码,演示了如何根据消息的ID查找处理函数。
 

const AFX_MSGMAP* pMessageMap; pMessageMap &#61; GetMessageMap();UINT iHash; iHash &#61; (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);AfxLockGlobals(CRIT_WINMSGCACHE);AFX_MSG_CACHE* pMsgCache; pMsgCache &#61; &_afxMsgCache[iHash];const AFX_MSGMAP_ENTRY* lpEntry;if (message &#61;&#61; pMsgCache->nMsg && pMessageMap &#61;&#61; pMsgCache->pMessageMap){// cache hitlpEntry &#61; pMsgCache->lpEntry;AfxUnlockGlobals(CRIT_WINMSGCACHE);if (lpEntry &#61;&#61; NULL)return FALSE;// cache hit, and it needs to be handledif (message <0xC000)goto LDispatch;elsegoto LDispatchRegistered;}else{// not in cache, look for itpMsgCache->nMsg &#61; message;pMsgCache->pMessageMap &#61; pMessageMap;#ifdef _AFXDLLfor (/* pMessageMap already init&#39;ed */; pMessageMap->pfnGetBaseMap !&#61; NULL;pMessageMap &#61; (*pMessageMap->pfnGetBaseMap)())#elsefor (/* pMessageMap already init&#39;ed */; pMessageMap !&#61; NULL;pMessageMap &#61; pMessageMap->pBaseMap)#endif{// Note: catch not so common but fatal mistake!!// BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)#ifdef _AFXDLLASSERT(pMessageMap !&#61; (*pMessageMap->pfnGetBaseMap)());#elseASSERT(pMessageMap !&#61; pMessageMap->pBaseMap);#endifif (message <0xC000){// constant window messageif ((lpEntry &#61; AfxFindMessageEntry(pMessageMap->lpEntries,message, 0, 0)) !&#61; NULL){pMsgCache->lpEntry &#61; lpEntry;AfxUnlockGlobals(CRIT_WINMSGCACHE);goto LDispatch;}}else{// registered windows messagelpEntry &#61; pMessageMap->lpEntries;while ((lpEntry &#61; AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) !&#61; NULL){UINT* pnID &#61; (UINT*)(lpEntry->nSig);ASSERT(*pnID >&#61; 0xC000 || *pnID &#61;&#61; 0);// must be successfully registeredif (*pnID &#61;&#61; message){pMsgCache->lpEntry &#61; lpEntry;AfxUnlockGlobals(CRIT_WINMSGCACHE);goto LDispatchRegistered;}lpEntry&#43;&#43;; // keep looking past this one}}}pMsgCache->lpEntry &#61; NULL;AfxUnlockGlobals(CRIT_WINMSGCACHE);return FALSE;}LDispatch:

注意

  1. 对查找结果的缓存可以提高查找的效率。
  2. 不要被MFC起的名字欺骗了&#xff0c;从数据结构上来说&#xff0c;查找是顺序的&#xff0c;而不是使用CMap类使用的散列技术&#xff0c;所以上面的代码中使用散列技术&#xff0c;缓存最近的查找结果和把最常用的映射项放在最前面通常有助于提高效率。

使用查找映射的好处是&#xff0c;可以方便地在派生类中扩展和覆盖映射&#xff08;例如重新实现IDispatch&#xff09;&#xff0c;而不用重写/重载查找函数&#xff08;消息和命令的分发&#xff0c;或者接口的查询&#xff09;&#xff1b;也可以不使用对资源消耗很大的虚函数表。&#xff08;尽管如此&#xff0c;CWnd类还是有无数个虚函数&#xff0c;并且不出意外地看到&#xff0c;在MFC6到MFC7的升级中又有增加&#xff09;

使用查找映射的坏处么&#xff0c;当然是理解上的问题和性能上的损失了。
 

句柄映射

MFC在把句柄封装成对象方面不遗余力&#xff0c;为了保证同一线程内对象<->句柄映射是一对一的&#xff0c;创建了各种各样的句柄映射&#xff0c;窗口&#xff0c;GDI对象&#xff0c;菜单诸如此类。为了封装GetDlgItem,SelectObject这样的API返回的临时的句柄&#xff0c;MFC还产生临时的对象<->句柄映射。句柄映射使得GetParentFrame这样的函数可以实现。

CFrameWnd* CWnd::GetParentFrame() const{if (GetSafeHwnd() &#61;&#61; NULL) // no Window attachedreturn NULL;ASSERT_VALID(this);CWnd* pParentWnd &#61; GetParent(); // start with one parent upwhile (pParentWnd !&#61; NULL){if (pParentWnd->IsFrameWnd())return (CFrameWnd*)pParentWnd;pParentWnd &#61; pParentWnd->GetParent();}return NULL;}

_AFXWIN_INLINE CWnd* CWnd::GetParent() const{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetParent(m_hWnd)); }

看到了么&#xff0c;它首先调用API GetParent&#xff0c;然后去本线程的窗口<->句柄映射查找对象指针&#xff0c;然后调用CWnd::IsFrameWnd来决定对象是否是框架。&#xff08;谢天谢地&#xff0c;这个函数是用虚函数而不是用CObject::IsKindOf,不然又得遍历一遍运行时类信息&#xff09;。因为对象和句柄的对应的唯一性&#xff0c;所以可以找到唯一的父框架窗口对象。

在一些经常调用的函数里面也使用到这个映射

LRESULT CALLBACKAfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam){// special message which identifies the window as using AfxWndProcif (nMsg &#61;&#61; WM_QUERYAFXWNDPROC)return 1;// all other messages route through message mapCWnd* pWnd &#61; CWnd::FromHandlePermanent(hWnd);ASSERT(pWnd !&#61; NULL); ASSERT(pWnd->m_hWnd &#61;&#61; hWnd);if (pWnd &#61;&#61; NULL || pWnd->m_hWnd !&#61; hWnd)return ::DefWindowProc(hWnd, nMsg, wParam, lParam);return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);}

也就是说&#xff0c;它要遍历一遍afxMapHWND()返回的对象里面的永久的句柄映射。而这个函数在每个消息到达的时候都要调用。这是MFC应用程序性能损失的原因之一。

同样的&#xff0c;由于这些对象是被线程所拥有的&#xff0c;MFC的这些句柄映射的存储方式是线程局部存储&#xff08;thread-local-storage &#xff0c;TLS&#xff09;。也就是说&#xff0c;对于同一个句柄&#xff0c;句柄映射中相应的对象可以不一致。这在多线程程序中会造成一些问题&#xff0c;参见微软知识库文章Q147578 CWnd Derived MFC Objects and Multi-threaded Applications。

总结

MFC为了快速和方便地开发作了很多工作&#xff0c;例如上述的两种映射&#xff0c;但是性能方面有所损失。开发应用程序时&#xff0c;需要在快速方便和性能损失方面的权衡。&#xff08;话是这么说&#xff0c;但是根据摩尔定律&#xff0c;再过两年我的话就成废话了&#xff09;






推荐阅读
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • Python中sys模块的功能及用法详解
    本文详细介绍了Python中sys模块的功能及用法,包括对解释器参数和功能的访问、命令行参数列表、字节顺序指示符、编译模块名称等。同时还介绍了sys模块中的新功能和call_tracing函数的用法。推荐学习《Python教程》以深入了解。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 解决github访问慢的问题的方法集锦
    本文总结了国内用户在访问github网站时可能遇到的加载慢的问题,并提供了解决方法,其中包括修改hosts文件来加速访问。 ... [详细]
  • 本文介绍了Composer依赖管理的重要性及使用方法。对于现代语言而言,包管理器是标配,而Composer作为PHP的包管理器,解决了PEAR的问题,并且使用简单,方便提交自己的包。文章还提到了使用Composer能够避免各种include的问题,避免命名空间冲突,并且能够方便地安装升级扩展包。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 本文整理了Java中com.evernote.android.job.JobRequest.getTransientExtras()方法的一些代码示例,展示了 ... [详细]
author-avatar
呼吸的雨儿作_741
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有