初识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 (即:用于同一个作用域中);

浙公网安备 33010602011771号