进入MFC讲坛的前言(四)

简介: MFC的消息映射机制   MFC的设计者们在设计MFC时,紧紧把握一个目标,那就是尽可能使得MFC的代码要小,速度尽可能快。为了这个目标,他们使用了许多技巧,其中很多技巧体现在宏的运用上,实现MFC的消息映射的机制就是其中之一。
MFC的消息映射机制 


  MFC的设计者们在设计MFC时,紧紧把握一个目标,那就是尽可能使得MFC的代码要小,速度尽可能快。为了这个目标,他们使用了许多技巧,其中很多技巧体现在宏的运用上,实现MFC的消息映射的机制就是其中之一。

  同MFC消息映射机制有关的宏有下面几个:

  DECLARE_MESSAGE_MAP()宏

  BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏

  弄懂MFC消息映射机制的最好办法是将找出一个具体的实例,将这些宏展开,并找出相关的数据结构。

  DECLARE_MESSAGE_MAP()

  DECLARE_MESSAGE_MAP()宏的定义如下:

  #define DECLARE_MESSAGE_MAP() \

  private: \

  static const AFX_MSGMAP_ENTRY _messageEntries[]; \

  protected: \

  static AFX_DATA const AFX_MSGMAP messageMap; \

  virtual const AFX_MSGMAP* GetMessageMap() const; \

从上面的定义可以看出,DECLARE_MESSAGE_MAP()作下面三件事:

  定义一个长度不定的静态数组变量_messageEntries[];

  定义一个静态变量messageMap;

  定义一个虚拟函数GetMessageMap();

在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中两个对外不公开的数据结构

AFX_MSGMAP_ENTRY和AFX_MSGMAP。为了弄清楚消息映射,有必要考察一下这两个数据结构的定义。

  AFX_MSGMAP_ENTRY的定义

  struct AFX_MSGMAP_ENTRY

  {

   UINT nMessage; // windows message

   UINT nCode; // control code or WM_NOTIFY code

   UINT nID; // control ID (or 0 for windows messages)

   UINT nLastID; // used for entries specifying a range of control id's

   UINT nSig; // signature type (action) or pointer to message #

   AFX_PMSG pfn; // routine to call (or special value)

  };

结构中各项的含义注释已经说明得很清楚了,这里不再多述,从上面的定义你是否看出,AFX_MSGMAP_ENTRY结构实际上定义了消息和处理此消息的动作之间的映射关系。因此静态数组变量_messageEntries[]实际上定义了一张表,表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系,因而这张表也称为消息映射表。再看看AFX_MSGMAP的定义。

  (2)AFX_MSGMAP的定义

  struct AFX_MSGMAP

  {

   const AFX_MSGMAP* pBaseMap;

   const AFX_MSGMAP_ENTRY* lpEntries;

   };

不难看出,AFX_MSGMAP定义了一单向链表,链表中每一项的值是一指向消息映射表的指针(实际上就是_messageEntries的值)。通过这个链表,使得在某个类中调用基类的的消息处理函数很容易,因此,“父类的消息处理函数是子类的缺省消息处理函数”就“顺理成章”了。在后面的“MFC窗口的消息处理”一节中会对此作详细的讲解。

  BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()

  它们的定义如下:

  #define BEGIN_MESSAGE_MAP(theClass, baseClass) \

  const AFX_MSGMAP* theClass::GetMessageMap() const \

  { return &theClass::messageMap; } \

  AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \

  { &baseClass::messageMap, &theClass::_messageEntries[0] }; \

  AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \

  { \

   #define END_MESSAGE_MAP() \

   {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \

   }; \

对应BEGIN_MESSAGE_MAP()的定义可能不是一下子就看得明白,不过不要紧,举一例子就很清楚了。对于BEGIN_MESSAGE_MAP(CView, CWnd),VC预编译器将其展开成下面的形式:

  const AFX_MSGMAP* CView::GetMessageMap() const

  { 

   return &CView::messageMap; 

   }

  AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap =

  { 

   &CWnd::messageMap, 

   &CView::_messageEntries[0] 

  };

  AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries[] =

  {

  至于END_MESSAGE_MAP()则不过定义了一个表示映射表结束的标志项,我想大家对于这种简单的技巧应该是很熟悉的,无需多述。

到此为止,我想大家也已经想到了象ON_COMMAND这样的宏的具体作用了,不错它们只不过定义了一种类型的消息映射项,看看ON_COMMAND的定义:

  #define ON_COMMAND(id, memberFxn) \

  { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn },

   根据上面的定义,ON_COMMAND(ID_FILE_NEW, OnFileNew)将被VC预编译器展开

   如下:

  {WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, 

  (AFX_PMSG)&OnFileNew},

到此,MFC的消息映射机制已经清楚了,现在提出并解答两个问题以作为对这一节的小结。

  为什么不直接使用虚拟函数实现消息处理函数呢?这是一个GOOD QUESTION。前面已经说过,MFC的设计者们在设计MFC时有一个很明确的目标,就是使得“MFC的代码尽可能小,速度尽可能快”,如果采用虚拟函数,那么对于所有的窗口消息,都必须有一个与之对应的虚拟函数,因而对每一个从CWnd派生的类而言,都会有一张很大的虚拟函数表vtbl。但是在实际应用中,一般只对少数的消息进行处理,大部分都交给系统缺省处理,所以表中的大部分项都是无用项,这样做就浪费了很多内存资源,这同MFC设计者们的设计目标是相违背的。当然,MFC所使用的方法只是解决这类问题的方式之一,不排除还有其他的解决方式,但就我个人观点而言,这是一种最好的解决方式,体现了很高的技巧性,值得我们学习。

  至于这第二个问题,是由上面的问题引申出来的。如果在子类和父类中出现了相同的消息出来函数,VC编译器会怎么处理这个问题呢?VC不会将它们看作错误,而会象对待虚拟函数类似的方式去处理,但对于消息处理函数(带afx_msg前缀),则不会生成虚拟函数表vtbl。
目录
相关文章
|
API 调度 开发工具
03 MFC - 入门程序
03 MFC - 入门程序
56 0
|
Linux API C++
Qt学习前言
奇趣科技(Trolltech)是一家什么样的公司?答:QT开发库的母公司。   完全基于Linux真正成长起来的公司仍然寥寥无几,而奇趣试图在开源里找到一条独特的发展之路。   David Almstrom,一位头发卷曲的瑞典人,奇趣科技(Trolltech)中国区首席代表,再次见到是在他那间中国风极为浓厚的办公室。
1398 0
|
分布式数据库 Windows
|
前端开发 vr&ar C++
进入MFC讲坛的前言(五)
框窗、视图和文档及其关系   MFC架构的另外一个特色是它的框窗、视图和文档这个三位一体的结构,它是一个典型的MVC(Model、View and Controler)结构。严格的讲,框窗不属于MVC中的任何一项,MFC设计者将框窗加进来是为了能更好的协调文档 和视图。
783 0
进入MFC讲坛的前言(三)
MFC中的窗口创建及窗口消息映射 我经常碰到有人问我有关窗口创建的问题,他们经常把用HWND描述的系统窗口对象和用CWnd描述的MFC的窗口对象混淆不清。这两者之间是紧密联系在一起的,但是MFC为了自身的管理,在CWnd中加了一些额外的内容,包括如何从HWND生成CWnd。
919 0