初识MFC:仿写消息映射表

  单纯的C++学习,已经有一个阶段了。没有从刚开始学习code就写博客记录下来,有点小小的遗憾!那既然已经决定了,就从现在学的MFC开始吧!

  (至于是怎么用仿写消息映射表这个小实践从单纯的C++过渡到MFC的,这个说来话长,就不解释了)

 

以下是在win32控制台下对MFC消息映射表的仿写的部分代码段:

 1 //头文件---AfxWin.h
 2 #pragma once
 3 
 4 #define WM_LBUTTONDOWN    1
 5 #define WM_RBUTTONDOWN    2
 6 
 7 //用于标示message ID
 8 enum {CView,CFrame,CDialog};
 9 
10 class CMyWnd;
11 typedef void (CMyWnd::*AFX_PMSG)(void); //用于定义装MsgMap中函数的函数指针
12 
13 //标示消息映射表中的函数在(union MessageMapFuntions)中所对应的位置编号
14 enum AfxSig{AfxSig_end,AfxSig_v_i,AfxSig_v_ii};
15 
16 
17 
18 
19 union MessageMapFuntions        //  所有函数的 函数原型
20 {                                    //  把函数指针转型的
21     AFX_PMSG pfn;          
22     void (CMyWnd::*pfn_v_i)(int);
23     void (CMyWnd::*pfn_v_ii)(int,int);
24 };
25 
26 struct AFX_MSGMAP_ENTRY   // 消息映射表入口
27 {
28     int nMessage;   //  装一个消息
29     AFX_PMSG pfn;   //  这个消息的处理函数 (负责装地址)
30     AfxSig nSig;          // 这个函数的调用方式
31 };
32 
33 struct AFX_MSGMAP   // 消息映射表节点
34 {
35     const AFX_MSGMAP_ENTRY* pEntries;  //  装 自己的消息映射表
36     const AFX_MSGMAP* (*pfnGetBaseMessageMap)();  //  得到父类的 消息映射表的节点 (通过函数得到的)
37 };
38 
39 //消息结构体
40 struct MSG
41 {
42     int nMessage;     //  这个消息叫什么
43     int hwnd;         //  这个消息发给哪个窗口的
44     int nTime;       //  这个消息发送的时间
45     int lParam;       // 消息的数据
46     int wParam;   
47 };

 

 1 //此段代码为将操作函数装入消息映射表中
 2     //已经提前在类中定义了方法
 3     //    public:
 4     //        virtual const AFX_MSGMAP* GetMessageMap();
 5     //        static const AFX_MSGMAP* GetThisMessageMap();     //   装消息映射表
 6 
 7 const AFX_MSGMAP* CMyWnd::GetMessageMap()
 8 {
 9     return GetThisMessageMap();
10 }
11 const AFX_MSGMAP* CMyWnd::GetThisMessageMap()
12 {
13     //创建一个消息映射表 注:MFC中,这些是被封宏的
14     static const AFX_MSGMAP_ENTRY pEntries[] = {
15         {WM_LBUTTONDOWN,(AFX_PMSG)(void (CMyWnd::*)(int))&CMyWnd::OnLButtonDown,AfxSig_v_i},
16         {WM_RBUTTONDOWN,(AFX_PMSG)(void (CMyWnd::*)(int,int))&CMyWnd::OnRButtonDown,AfxSig_v_ii},
17         {0,0,AfxSig_end}
18     };
19     //  消息映射表节点
20     static const AFX_MSGMAP msgmap = {&pEntries[0],NULL};
21     return &msgmap;
22 }
 1 //以下代码是将函数实现的开头及结尾进行封宏
 2 #define BEGIN_MESSAGE_MAP(theClass, baseClass) \
 3     PTM_WARNING_DISABLE \
 4     const AFX_MSGMAP* theClass::GetMessageMap() const \
 5 { return GetThisMessageMap(); } \
 6     const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
 7 { \
 8     typedef theClass ThisClass;                           \
 9     typedef baseClass TheBaseClass;                       \
10     static const AFX_MSGMAP_ENTRY _messageEntries[] =  \
11 {
12 
13 #define END_MESSAGE_MAP() \
14 {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
15 }; \
16     static const AFX_MSGMAP messageMap = \
17 { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
18     return &messageMap; \
19 }                                  \
20     PTM_WARNING_RESTORE
21 
22 //以下代码是将向消息映射表入口中添加操作函数的过程进行封宏,以实现添加
23     //以添加命令消息函数为例
24 #define ON_COMMAND(id, memberFxn) \
25 { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
26     static_cast<AFX_PMSG> (memberFxn) },

 

 

Q1:消息映射表是什么时候被创建的?

A1:显然这里的pEntries[]代表着消息映射表入口,此结构体数组为静态,则在编译的时候被创建,又因为是在函数GetThisMessage中定义,所以只能通过函数取出。

Q2:没写之前,我又在思考以什么方式创建这个消息映射表?

A2:最开始,我设想的是利用动态创建的方法创建这个映射表以实现添加,但是当我看到可以用封宏这种方式进行操作的时候,果断放弃了最初的想法。

Q3:将操作函数放入消息映射表时,函数地址被强转了两次,为什么?

A3:函数指针类型:AFX_PMSG属于CWnd类,若是在其他非CWnd类(如CDialog)中往MsgMap添加操作函数,如果函数带有参数,则相当于先把参数转换成一样的,再把类名转换成一样的。(注:也可以先转换类名,在转换参数),也就是说函数指针转换过程中,每次只能转换一方面。

Q4:类内的静态成员与其派生类同名函数的关系?

A4:情况同非静态成员函数(存在隐藏机制),测试代码如下:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class AA
 5 {
 6 public:
 7     static void fun(int)
 8     {
 9         cout << "AA" << endl;
10     }
11 };
12 
13 class BB : public AA
14 {
15 public:
16     /*static*/ void fun()
17     {
18         cout << "BB" << endl;
19     }
20 };
21 
22 int main()
23 {
24 
25     AA a;
26     a.fun(1);
27     BB b;
28     b.fun();    //若在BB类中写了同名(静态)函数fun,则相当于把基类中的函数fun隐藏了
29     //b.funA(1); //错误写法
30     cout << "通过类名访问:" << endl;
31     b.AA::fun(1);
32 
33     //cout << "若将BB类中的静态函数fun注释掉:" << endl;
34     //b.fun(1);    //这时候,派生类与基类共用一个静态的fun
35     getchar();
36     return 0;
37 }

 

注意点:

1.函数指针强转时,当类名、参数均不同,则需要强转两次;

2.消息映射表入口是一个结构体数组,可以把数组首元素给变量;

3.消息映射表是一个结构体,可以通过其内函数指针连接父类的消息映射表;

4.类的成员函数指针调用方式:->*;

5.封宏过程中,若两个宏放的位置相邻,后边的宏想用前边的宏的参数,可在前宏中加typedef  (即:用于同一个作用域中);

 

posted @ 2015-04-25 20:12  netosoul  阅读(366)  评论(0)    收藏  举报