`

MFC为何使用消息映射表而不用虚函数?

    博客分类:
  • C++
阅读更多
这个问题是windows开发面试中最经常问到得问题,也是很有深度的一个问题。
有两个帖子对该问题讨论的比较深刻:
http://topic.csdn.net/u/20090822/16/4cf5d189-0e5e-41ff-9ba3-c7eaf2f6da74.html
http://topic.csdn.net/u/20090316/22/8b067591-6a17-4970-b224-41ab589294b3.html

说法一:
虚函数实现占用内存较大
      侯捷在《深入浅出MFC》中说微软使用消息映射机制而不用虚函数,是因为虚函数空间代价的原因。在当前MFC2.0版本发布的时候是92年,pc的内存才几M。一个类的虚表的大小就是虚函数的个数*一个指针的大小。
假设windows的通用消息有200个,那么CWnd类的虚表就有 200*4个byte = 800byte

      CWnd类的所有派生类均copy了一份CWnd的虚表vtable,然后自己的虚函数往后加CWnd类的虚表的后头。
(至于有人说CWnd类的派生类能共享CWnd的虚表,这个说法不靠谱。因为派生类自己的虚函数值加在基类的虚函数表项的最后的。如果CWnd派生了CWndChildA 和 CWndChildB,且两个孩子均有自己的虚函数,那么都往CWnd类的后面加,岂不是冲突了?)

     也就是系统内所有的CWnd类的派生类都要承受 800byte的代价。假设有100个类派生自CWnd 那么代价就是800*100byte 也就是 80K。这在当时内存很紧张的情况下,已经是一种巨大的内存消耗了!这里需要注意一点:vtable是和类绑定在一起的,而不是和类对象(也叫类的实例)绑定在一起的,类的实例仅增加一个指向该向类的vtable的指针而已。也就是说,如果你有100个CWnd派生类,哪怕你生成了100000个派生类的实例,vtable占用的内存也是80K。

     看来在当时的环境看来,MFC没有采用虚函数,内存的确是一个考虑。
但是放在现在看,这点内存消耗确实微不足道的! 也就是说,如果现在重新设计MFC的消息机制,如果不采用虚函数,并非因为虚函数的空间浪费问题。

结论:这个说法靠谱。

说法二:
消息映射机制效率比虚函数效率高。
      因为那么多消息ID,如果找到其对应的消息处理函数,switch是不可少的!(可以hash?哦哦,的确可能,不过mfc里面可没这么做?mfc里面怎么做的我也不清楚)
MFC中采用的是消息映射的机制,而没有用虚函数的机制,因为消息有很多,如果用虚函数机制,需要给每个消息定义一个虚函数,在分派消息时,程序需要逐一判断是哪一个消息,找到合适的分支后再调用相应的虚函数;而通常情况下,应用程序不需要响应太多的消息,消息映射方式只需要判断程序想要响应的这些消息即可,所以开销小。
也就是说,MFC采用了消息映射而没有采用虚函数,是从对消息的响应机制来考虑的。 消息映射,就可以仅实现自己感兴趣的消息,这样switch时就可以快一点。

      不过话又说回来,对一个非自己感兴趣的系统消息来了以后,就需要遍历消息网,层层的向基类查找直到找到对应的消息处理函数!这本身也很浪费时间!也许这种情况比较少见吧,否则的话,消息映射的消息响应时间并不比虚函数来的快!因为虚函数最多只需一次遍历,而且,如果可以采用hash技术,更快!
如果说,大多数消息都是系统的消息,那么消息映射的迭代查找消息函数的方式并不比虚函数的switch来的快!
PS:这里有一篇对比消息映射机制和虚函数机制效率的简单模拟实验
http://blog.csdn.net/hjsunj/archive/2008/01/10/2034314.aspx

结论,该说法不靠谱!

说法三:
为了未来的可扩展性。兼容新的系统级的消息。

      我不是很清楚MS设计消息映射的初衷,但是感觉它着眼点更侧重于增加新消息很容易,而不是节省内存。
如果我们使用虚函数机制实现,恐怕对于每个可能的消息我们都必须在基类中定义一个虚函数,而其首要的困难就是你无法猜测未来会出现什么消息,也无法确定需要定义什么样函数原型的虚函数。而使用消息映射,解决这个问题则相对容易,因为这将由未来的程序设计者决定他们的消息该如何处理。
     对于系统的新增消息,消息映射支持起来较方便。虚函数想要支持就需要改动基类添加虚函数。
对于自定义的消息,无论消息映射和虚函数都可以很好的支持。
那么虚函数方式如何支持自定义消息?
    自定义消息是不需要加到基类的。基类可以加个虚函数,OnMessage(xxx), 然后有自定义消息的类实现之,用switch转换成相应虚函数调用,不是自己的消息再传给基类。

结论:这个说法靠谱。

分享到:
评论

相关推荐

    MFC教程入门知识全集.rar

    4.5.3 消息映射表 4.5.4 如何添加消息映射 4.6 ClssWizard 的使用 4.6.1 ClssWizard 概貌 4.6.2 如何添加消息处理函数 4.6.3 如何添加成员变量 4.6.4 如何添加一个新类 第5 章 图形与文本 5.1 理解图形设备...

    MFC的程序框架剖析

    什么是句柄?...也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。...

    MFC常用的类及其成员函数简介

    MFC应用程序为每个CCmdTarget派生类创建一个称为消息映射表的静态数据结构,可将消息映射到对象所对应的消息处理函数上。 (2)设置光标 BeginWaitCursor() 将光标改为沙漏形状; EndWaitCursor() 将光标改回到...

    77G 22套C语言 C++ 数据结构 程序设计视频课程合集 C丨C++相关学习视频全套视频教程

    4.MFC_消息映射.mp4 40.MFC_模态对话框.mp4 41.MFC_非模态对话框.mp4 42.MFC_属性表.mp4 43.MFC_公用对话框.mp4 44.MFC_数组类-1.mp4 45.MFC_数组类-2.mp4 46.MFC_CArray.mp4 47.MFC_列表类.mp4 48.MFC_...

    VC之美化界面篇本文专题讨论VC中的界面美化,适用于具有中等VC水平的读者。读者最好具有以下VC基础:

    3. 利用MFC类的虚函数机制,重载有用的虚函数。在MFC框架调用该函数的时候,重新定义它的状态和行为; 一般来说,应用程序可以通过以下两种途径来实现以上的方法: 1. 在父窗口里,截获自身的或者由子元素(包括...

    c++面试题基础分享.doc

    46.虚函数是怎么实现的 47.什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法? 48.变量的声明和定义有什么区别 49.sizeof是一个操作符,strlen是库函数 50.写一个“标准”宏MIN 51.简述strcpy sprintf与...

    Visual C++ 2005入门经典.part08.rar (整理并添加所有书签)

    9.6.3 使用引用处理虚函数 9.6.4 纯虚函数 9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和...

    Visual C++ 2005入门经典.part04.rar (整理并添加所有书签)

    9.6.3 使用引用处理虚函数 9.6.4 纯虚函数 9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和...

    Visual C++ 2005入门经典.part07.rar (整理并添加所有书签)

    9.6.3 使用引用处理虚函数 9.6.4 纯虚函数 9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和...

    Visual C++ 2005入门经典.part09.rar (整理并添加所有书签)

    9.6.3 使用引用处理虚函数 9.6.4 纯虚函数 9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和...

    Visual C++ 2005入门经典.part06.rar (整理并添加所有书签)

    9.6.3 使用引用处理虚函数 9.6.4 纯虚函数 9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和...

    Visual C++ 2005入门经典.part05.rar (整理并添加所有书签)

    9.6.3 使用引用处理虚函数 9.6.4 纯虚函数 9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和...

    OnPrepareDC

    许多MFC库函数只能在设备坐标下工作(尤其CRect类成员函数)。可以认为CDC的所有成员函数都一逻辑坐标作参数。可以认为CWnd的成员函数都以设备...在CView的虚函数OnPrepareDC中设置映射模式要比在OnDraw函数中要好。

    Windows编程技术

    10.4.3 为控件添加消息映射 192 10.4.4 为控件添加成员变量 193 10.4.5 使用CDialog派生类对象 194 10.5 基于对话框的MFC程序 194 10.5.1 建立项目 195 10.5.2 对话框程序框架分析 196 10.6 通用对话框 202 10.6.1 ...

    Visual C++ 2005入门经典--源代码及课后练习答案

    9.6.3 使用引用处理虚函数 469 9.6.4 纯虚函数 470 9.6.5 抽象类 471 9.6.6 间接基类 474 9.6.7 虚析构函数 476 9.7 类类型之间的强制转换 481 9.8 嵌套类 482 9.9 C++/CLI编程 485 9.9.1 C++/CLI...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

     ·举例说明如何使用mfc开发本地windows应用程序  ·指导读者用c++和c++/cli设计和创建大量的windows应用程序  ·为帮助读者掌握编程技巧,提供了大量可运行的示例和练习 作译者  Ivor Horton是撰著Java、C...

Global site tag (gtag.js) - Google Analytics