MFC的消息映射原理

本文要求对C++语法比较熟悉(特别是虚函数的使用),若不熟悉建议参阅《C++语法详解》一书,电子工业出版社出版

1、消息映射:就是把指定的消息交给指定的函数进行处理的方法,这样就形成了一个<消息,处理函数>对。

2、本文有时会使用<M,F>表示<消息,处理函数>对。

一、共用体(union)的使用

1、共用体可以实现以下两个功能(详见示例说明)。

1)、调用函数时不需要知道函数的名称(通过函数指针调用),以及确定调用该函数时需要传递什么类型的形参(即可以确定函数原型)。

2)、在类继承体系中实现类似虚函数的功能。

2、语法问题(指向类成员的指针):使用指向类成员的函数指针时,必须通过类的对象或指向类的指针间接使用,比如class A{public:void f(){}}; A m; A *pa=&m; void (A::*pf)()=&A::f; 则应这样通过pf调用f,即(ma.*pf)()或(pa->*pf)();是正确的,但不能直接使用pf调用成员函数f,即(*pf)();或(A::*pf)();错误。

示例3.5:共用体的使用(以下示例为C++程序)
#include “stdafx.h” //对于C++程序,VC++编译器必须包含此头文件。
#include
using namespace std;
class B;
typedef void (B::*PF)();
class B{public:void f2(int,int){cout<<“FB”<<endl;} };
class C:public B{public:void f3(int){cout<<“FC”<<endl;} };
class D:public C{public:void f4(int,int){cout<<“FD”<<endl;}
static PF pf; static PF pf1; };
PF D::pf=(PF)&f3;
PF D::pf1=(PF)&f4;/定义静态成员,此时pf指向C::f3的地址,pf1指向D::f4的地址。/
union UN{PF uf; //uf用于存储类D中pf和pf1的地址。
void (B::pf_0)(int,int); /通过共用体的成员pf_0调用相关的函数时,该函数必须具有void
(B
:😃(int,int)形式的原型。
/
void (B::*pf_1)(int);}; /因为UN是共用体,因此其成员uf,pf_0,pf_1拥有相同的值,即他们都是指向同一地址的指针,但是三个函数指针的类型不一样(即指向的函数的形参和返回类型不一样)。/
void main()
{ B *pb; C mc; D md;
pb=&md; UN meff;
//以下步骤可通过传递给meff.uf的函数的形参的不同,而选择调用meff中的不同成员函数。
meff.uf=D::pf1; /meff.uf=D::pf1=(PF)&f4;可见,此时uf指向的是D::f4函数的地址/
(pb->meff.pf_0)(1,2); /输出FD,因为meff是共用体对象,因此meff.uf,meff.pf_0,meff.pf_1是指向的相同地址的指针,即都指向的是uf所指向的D::f4函数的地址。 但是meff.pf_0的类型为void (B:😃(int,int),而meff.pf_0的类型为void (B:😃(int),因此(pb->meff.pf_0)(1,2)该语句是在调用地址为D::f4处的函数,也就是调用类D中的函数f4,这就实现了通过父类的指针,间接的调用子类D中的成员函数f4,从而间接实现了虚函数的功能。此处应注意使用指向类成员函数的指针调用成员函数的语法。/
//(pb->meff.pf_0)(1,2); //错误,实参太少,因为meff.pf_0的类型为void (B::pf_0)(int,int)
meff.uf=D::pf; //此时uf指向C::f3,其原理同上
(pb->*meff.pf_1)(1);} //输出FC,其原理同上。
#include “stdafx.h” //对于C++程序,VC++编译器必须包含此头文件。
#include
using namespace std;
class B;
typedef void (B::*PF)();
class B{public:void f2(int,int){cout<<“FB”<<endl;} };
class C:public B{public:void f3(int){cout<<“FC”<<endl;} };
class D:public C{public:void f4(int,int){cout<<“FD”<<endl;}
static PF pf; static PF pf1; };
PF D::pf=(PF)&f3;
PF D::pf1=(PF)&f4;/定义静态成员,此时pf指向C::f3的地址,pf1指向D::f4的地址。/
union UN{PF uf; //uf用于存储类D中pf和pf1的地址。
void (B::pf_0)(int,int); /通过共用体的成员pf_0调用相关的函数时,该函数必须具有void
(B
:😃(int,int)形式的原型。
/
void (B::*pf_1)(int);}; /因为UN是共用体,因此其成员uf,pf_0,pf_1拥有相同的值,即他们都是指向同一地址的指针,但是三个函数指针的类型不一样(即指向的函数的形参和返回类型不一样)。/
void main()
{ B *pb; C mc; D md;
pb=&md; UN meff;
//以下步骤可通过传递给meff.uf的函数的形参的不同,而选择调用meff中的不同成员函数。
meff.uf=D::pf1; /meff.uf=D::pf1=(PF)&f4;可见,此时uf指向的是D::f4函数的地址/
(pb->meff.pf_0)(1,2); /输出FD,因为meff是共用体对象,因此meff.uf,meff.pf_0,meff.pf_1是指向的相同地址的指针,即都指向的是uf所指向的D::f4函数的地址。 但是meff.pf_0的类型为void (B:😃(int,int),而meff.pf_0的类型为void (B:😃(int),因此(pb->meff.pf_0)(1,2)该语句是在调用地址为D::f4处的函数,也就是调用类D中的函数f4,这就实现了通过父类的指针,间接的调用子类D中的成员函数f4,从而间接实现了虚函数的功能。此处应注意使用指向类成员函数的指针调用成员函数的语法。/
//(pb->meff.pf_0)(1,2); //错误,实参太少,因为meff.pf_0的类型为void (B::pf_0)(int,int)
meff.uf=D::pf; //此时uf指向C::f3,其原理同上
(pb->*meff.pf_1)(1);} //输出FC,其原理同上。
#include “stdafx.h” //对于C++程序,VC++编译器必须包含此头文件。
#include
using namespace std;
class B;
typedef void (B::*PF)();
class B{public:void f2(int,int){cout<<“FB”<<endl;} };
class C:public B{public:void f3(int){cout<<“FC”<<endl;} };
class D:public C{public:void f4(int,int){cout<<“FD”<<endl;}
static PF pf; static PF pf1; };
PF D::pf=(PF)&f3;
PF D::pf1=(PF)&f4;/定义静态成员,此时pf指向C::f3的地址,pf1指向D::f4的地址。/
union UN{PF uf; //uf用于存储类D中pf和pf1的地址。
void (B::pf_0)(int,int); /通过共用体的成员pf_0调用相关的函数时,该函数必须具有void
(B
:😃(int,int)形式的原型。
/
void (B::*pf_1)(int);}; /因为UN是共用体,因此其成员uf,pf_0,pf_1拥有相同的值,即他们都是指向同一地址的指针,但是三个函数指针的类型不一样(即指向的函数的形参和返回类型不一样)。/
void main()
{ B *pb; C mc; D md;
pb=&md; UN meff;
//以下步骤可通过传递给meff.uf的函数的形参的不同,而选择调用meff中的不同成员函数。
meff.uf=D::pf1; /meff.uf=D::pf1=(PF)&f4;可见,此时uf指向的是D::f4函数的地址/
(pb->meff.pf_0)(1,2); /输出FD,因为meff是共用体对象,因此meff.uf,meff.pf_0,meff.pf_1是指向的相同地址的指针,即都指向的是uf所指向的D::f4函数的地址。 但是meff.pf_0的类型为void (B:😃(int,int),而meff.pf_0的类型为void (B:😃(int),因此(pb->meff.pf_0)(1,2)该语句是在调用地址为D::f4处的函数,也就是调用类D中的函数f4,这就实现了通过父类的指针,间接的调用子类D中的成员函数f4,从而间接实现了虚函数的功能。此处应注意使用指向类成员函数的指针调用成员函数的语法。/
//(pb->meff.pf_0)(1,2); //错误,实参太少,因为meff.pf_0的类型为void (B::pf_0)(int,int)
meff.uf=D::pf; //此时uf指向C::f3,其原理同上
(pb->*meff.pf_1)(1);} //输出FC,其原理同上。
按下鼠标左键后弹出的消息框如下图(省略主窗口):

程序算法步骤详解:

1、使用SetWindowLongPtr函数重新设置MFC程序的过程函数。

2、把需要处理的<消息,处理函数>对(即<M,F>),抽像为一个类型,假设使用结构体类型S进行表示,那么每个结构体类型变量都会保存有一个相对应的<M,F>。比如:

typedef void (*PF)();

struct S{UINT msg;UINT msgid; PF pf;};

1)、msg表示需要处理的消息。

2)、msgid用于标示该结构体变量的一个id符号。该成员在本例无用处,但在后面会有用。

3)、pf表示用于处理消息msg的处理函数。

4)、为什么pf的类型是PF:因为消息处理函数的原型并不是全部一致的,在进行消息映射时应使用相同的函数原型形式(即PF的形式)以便对消息处理函数进行统一管理,因此在使用消息处理函数初始化该成员时需要把消息处理函数强制转换为PF类型。

3、创建一个结构体类型S的数组用于保存不同的<M,F>对,该数组就是程序员把消息指定给自定义函数进行处理的地方。比如:

LRESULT f1(WPARAM w, LPARAM l){return 0;} //处理消息的函数f1

LRESULT f(WPARAM w, LPARAM l){return 0;} //处理消息的函数f

const S ss[]={{WM_LBUTTONDOWN,1,PF(f1)},{WM_RBUTTONDOWN,2,PF(f)}, {0,0,(PF)0}};

1)、数组ss保存有两个<M,F>对,即处理鼠标左键按下消息的<WM_LBUTTONDOWN,f1>和处理鼠标右键按下消息的<WM_RBUTTONDOWN,f>。

2)、若程序员需要把其他消息使用另外的函数进行处理,则只需把相应的<消息,处理函数>对,添加到数组ss中即可,这样就实现了消息的映射。

3)、完成以上步骤之后,则在过程函数中接收到需要处理的消息时,只需调用“<M,F>”中的处理函数F处理该消息即可。问题的关键是怎样调用“处理函数F”。

4、使用共用体间接调用消息处理函数:

怎样调用相关联的消息处理函数:因为MFC的源码实现的消息映射是向程序员隐藏了的,那么在调用消息处理函数时,MFC源码肯定是不知道程序员自定义的“消息处理函数”的名称的,这就意味着,不能在源码中直接调用消息处理函数,而只能间接的调用类似以上数组ss中的结构体S中的成员pf,即只能这样调用消息处理函数ss[1].pf();但因为pf的原型与消息处理函数的原型并不相同(本例pf与f原型就不一致),这就可能会产生错误,为了解决函数原型的问题,可以使用共用体类型的成员保存消息处理函数的原型,然后使用共用体成员间接调用消息处理函数。比如:
union UN{PF pf; LRESULT (*pf_0)(WPARAM,LPARAM);LRESULT (*pf_1)(WPARAM,LPARAM);};
UN meff;
meff.pf=ss[0].pf; //初始化共用体变量meff,其中ss[0].pf指向的函数是f1。
meff.pf_0(w,l); //通过共用体成员pf_0间接调用消息处理函数f1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

曾忆少年时~峥嵘岁月稠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值