MFC虚函数mfc ocx控件回调函数数和消息因设函数的区别??求解释,各位大神!!!!

MFC的消息处理函数和消息过程函数的区别?_百度知道5146人阅读
MFC中应该有两类回调函数:一类是源自C的传统回调函数,此类回调函数若非定义为全局函数,而定义在类中的话,要添加static约束,常见的有EnumXXX();一类是消息响应函数,通过成员函数指针实现回调。
设想一种情况,基类A触发某事件E后,回调某定义好的函数F进行事件处理(MFC中表现为消息响应函数)。继承于类A的子类B和C,可能对于E有不同的处理方式,于是需要对基类A的函数F进行改写。自然而然的,我们想到将F定义为以virtual修饰的虚函数。
——————————————————————————————————————————————————————
回调函数的简单定义就是你定义的由Windows来调用。以下两个函数摘自《Programming
Windows with MFC》,这里暂且不管函数的具体作用,在FillListBox中有一个API函数,它调用的回调函数是EnumFontFamProc,回调函数的声明形式一般都是相对固定的,具体可以参考MSDN。
static int CALLBACK EnumFontFamProc (ENUMLOGFONT* lpelf,NEWTEXTMETRIC* lpntm, int nFontType, LPARAM lParam);
void CMainWindow::FillListBox ()
m_wndListBox.ResetContent ();
CClientDC dc (this);
::EnumFontFamilies ((HDC) dc, NULL, (FONTENUMPROC) EnumFontFamProc,(LPARAM) this);
int CALLBACK CMainWindow::EnumFontFamProc (ENUMLOGFONT* lpelf,NEWTEXTMETRIC* lpntm, int nFontType, LPARAM lParam)
CMainWindow* pWnd = (CMainWindow*) lP
if ((pWnd-&m_wndCheckBox.GetCheck () == BST_UNCHECKED) || (nFontType & TRUETYPE_FONTTYPE))
pWnd-&m_wndListBox.AddString (lpelf-&elfLogFont.lfFaceName);
请注意这里的函数EnumFontFamilies中的最后一个参数传递的是this即该CMainWindow对象的指针,为什么要这样呢,可以看到EnumFontFamProc的声明是static,在C++中static函数是不能调用非static成员的,所以这里传递一个this就不是很奇怪了。但是为什么要将该函数声明为static呢,这就要归咎于C++的特殊性了,众所周知C++编译器在编译的时候都会在对象中添加一个this指针,在成员函数调用中又会附加一个参数保存this指针,但是Windows的回调函数有严格的定义就是必须按照参数列表传递的参数,加了this指针后参数列表就会与Windows期望的参数列表不一致了,因此这里将其声明为static(static成员函数不会传递this指针,这点说起来总是知道,但是真正用时总是忘了,唉)。
&&&&&&&另外在Windows中使用callback函数很常见,恰好许多支持回调函数的API函数都像这里的EnumFontFamilies一样支持自定义的LPARAM参数,刚好可以传递this,如果使用的API函数不支持这样的自定义的LPARAM参数,就需要其他的方法了,一种比较简单的方法是将this复制为global变量使得回调函数可以使用。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:540418次
积分:6504
积分:6504
排名:第2567名
原创:170篇
转载:130篇
评论:49条
(2)(1)(19)(7)(1)(8)(2)(1)(2)(5)(3)(2)(17)(4)(3)(4)(7)(9)(16)(26)(33)(54)(42)(1)(3)(8)(4)(10)(1)(4)(1)使用原因/回调函数
回调函数相关图因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer() API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。另一个使用回调机制的API函数是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。不管怎么说,回调函数是继续自C语言的,因而,在C++中,应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚拟方法或函数符(functor),而不是回调函数。
函数实现/回调函数
代码实现回调函数相关图下面创建了一个sort.dll的动态链接库,它导出了一个名为CompareFunction的类型--typedef int (__stdcall *CompareFunction)(const byte*, const byte*),它就是回调函数的类型。另外,它也导出了两个方法:Bubblesort()和Quicksort(),这两个方法原型相同,但实现了不同的排序算法。void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc);void DLLDIR __stdcall Quicksort(byte* array,int size,int elem_size,CompareFunction cmpFunc);这两个函数接受以下参数:·byte * array:指向元素数组的指针(任意类型)。·int size:数组中元素的个数。·int elem_size:数组中一个元素的大小,以字节为单位。·CompareFunction cmpFunc:带有上述原型的指向回调函数的指针。这两个函数的会对数组进行某种排序,但每次都需决定两个元素哪个排在前面,而函数中有一个回调函数,其地址是作为一个参数传递进来的。对编写者来说,不必介意函数在何处实现,或它怎样被实现的,所需在意的只是两个用于比较的元素的地址,并返回以下的某个值(库的编写者和使用者都必须遵守这个约定):·-1:如果第一个元素较小,那它在已排序好的数组中,应该排在第二个元素前面。·0:如果两个元素相等,那么它们的相对位置并不重要,在已排序好的数组中,谁在前面都无所谓。·1:如果第一个元素较大,那在已排序好的数组中,它应该排第二个元素后面。基于以上约定,函数Bubblesort()的实现如下,Quicksort()就稍微复杂一点:void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc){for(int i=0; i < i++){for(int j=0; j < size-1; j++){//回调比较函数if(1 == (*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size)){//两个相比较的元素相交换byte* temp = new byte[elem_size];memcpy(temp, array+j*elem_size, elem_size);memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);memcpy(array+(j+1)*elem_size, temp, elem_size);delete []}}}}注意:因为实现中使用了memcpy(),所以函数在使用的数据类型方面,会有所局限。对使用者来说,必须有一个回调函数,其地址要传递给Bubblesort()函数。下面有二个简单的示例,一个比较两个整数,而另一个比较两个字符串:int __stdcall CompareInts(const byte* velem1, const byte* velem2){int elem1 = *(int*)velem1;int elem2 = *(int*)velem2;if(elem1 < elem2)return -1;if(elem1 > elem2)return 1;return 0;}int __stdcall CompareStrings(const byte* velem1, const byte* velem2){const char* elem1 = (char*)velem1;const char* elem2 = (char*)velem2;return strcmp(elem1, elem2);}下面另有一个程序,用于测试以上所有的代码,它传递了一个有5个元素的数组给Bubblesort()和Quicksort(),同时还传递了一个指向回调函数的指针。typedeint main(int argc, char* argv[]){int array[] = {, , 1098};cout << "Before sorting ints with Bubblesort\n";for(i=0; i < 5; i++)cout << array<< ’\n’;Bubblesort((byte*)array, 5, sizeof(array), &CompareInts);cout << "After the sorting\n";for(i=0; i < 5; i++)cout << array<< ’\n’;const char str = {"estella","danielle","crissy","bo","angie"};cout << "Before sorting strings with Quicksort\n";for(i=0; i < 5; i++)cout << str<< ’\n’;Quicksort((byte*)str, 5, 10, &CompareStrings);cout << "After the sorting\n";for(i=0; i < 5; i++)cout << str<< ’\n’;return 0;如果想进行降序排序(大元素在先),就只需修改回调函数的代码,或使用另一个回调函数,这样编程起来灵活性就比较大了。调用约定上面的代码中,可在函数原型中找到__stdcall,因为它以双下划线打头,所以它是一个特定于编译器的扩展,说到底也就是微软的实现。任何支持开发基于Win32的程序都必须支持这个扩展或其等价物。以__stdcall标识的函数使用了标准调用约定,为什么叫标准约定呢,因为所有的Win32API(除了个别接受可变参数的除外)都使用它。标准调用约定的函数在它们返回到调用者之前,都会从堆栈中移除掉参数,这也是Pascal的标准约定。但在C/C++中,调用约定是调用者负责清理堆栈,而不是被调用函数;为强制函数使用C/C++调用约定,可使用__cdecl。另外,可变参数函数也使用C/C++调用约定。Windows操作系统采用了标准调用约定(Pascal约定),因为其可减小代码的体积。这点对早期的Windows来说非常重要,因为那时它运行在只有640KB内存的电脑上。如果不喜欢__stdcall,还可以使用CALLBACK宏,它定义在windef.h中:1#define CALLBACK—stadcallor2#define CALLBACK PASCAL//而PASCAL在此被#define成__stdcall作为回调函数的C++方法因为平时很可能会使用到C++编写代码,也许会想到把回调函数写成类中的一个方法,但先来看看以下的代码:1class CCallbackTester2{3public:4int CALLBACK CompareInts( const byte* velem1,const byte* velem2 );5};6Bubblesort( (byte*)array,5,sizeof(array), &CCallbackTester::CompareInts );如果使用微软的编译器,将会得到下面这个编译错误:1error C2664: ’Bubblesort’ : cannot convert parameter 4 from ’int (__stdcall CCallbackTester::*)(const unsigned char *,const unsigned char *)’ to ’int (__stdcall *)(const unsigned char *,const unsigned char *)’ There is no context in which this conversion is possible
&|&相关影像
互动百科的词条(含所附图片)系由网友上传,如果涉嫌侵权,请与客服联系,我们将按照法律之相关规定及时进行处理。未经许可,禁止商业网站等复制、抓取本站内容;合理使用者,请注明来源于。
登录后使用互动百科的服务,将会得到个性化的提示和帮助,还有机会和专业认证智愿者沟通。
此词条还可添加&
编辑次数:8次
参与编辑人数:7位
最近更新时间: 23:37:35
贡献光荣榜【求助】使用消息机制和函数调用有什么区别呢?或者各有什么优缺点,我们该怎么选择使用?
[问题点数:40分,结帖人u]
【求助】使用消息机制和函数调用有什么区别呢?或者各有什么优缺点,我们该怎么选择使用?
[问题点数:40分,结帖人u]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2014年11月 VC/MFC大版内专家分月排行榜第三
2013年 总版技术专家分年内排行榜第三
2012年 总版技术专家分年内排行榜第七
2013年 总版技术专家分年内排行榜第三
2012年 总版技术专家分年内排行榜第七
2014年11月 VC/MFC大版内专家分月排行榜第三
2014年11月 VC/MFC大版内专家分月排行榜第三
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。您所在位置: &
&nbsp&&nbsp&nbsp&&nbsp
MFC中对各种函数尤其是虚函数的应用原理.doc45页
本文档一共被下载:
次 ,您可全文免费在线阅读后下载本文档。
文档加载中...广告还剩秒
需要金币:150 &&
MFC中对各种函数尤其是虚函数的应用原理,虚函数表的工作原理,虚函数实现原理,虚函数原理,虚函数的实现原理,虚函数的原理,c 虚函数原理,虚函数实现多态的原理,虚函数,c 虚函数
你可能关注的文档:
··········
··········
MFC对象的创建
前面几章介绍了MFC的核心概念和思想,即介绍了MFC对Windows对象的封装方法和特点;MFC对象的动态创建、序列化;MFC消息映射机制。
现在,考查MFC的应用程序结构体系,即以文档-视为核心的编程模式。学习本章,应该弄清楚以下问题:
MFC中诸多MFC对象的关系:应用程序对象,文档对象,边框窗口对象,文档边框窗口对象,视对象,文档模板对象等。
MFC对象的创建和销毁:由什么对象创建或销毁什么对象,何时创建,何时销毁?
MFC提供了那些接口来支持其编程模式?
MFC对象的关系
这里讨论应用程序、文档模板、边框窗口、视、文档等的创建关系。图5-1大略地表示了创建顺序,但表5-1更直接地显示了创建与被创建的关系。
表5-1 MFC对象的创建关系
被创建的对象
应用程序对象
视 交互作用关系
应用程序对象有一个文档模板列表,存放一个或多个文档模板对象;文档模板对象有一个打开文档列表,存放一个或多个已经打开的文档对象;文档对象有一个视列表,存放显示该文档数据的一个或多个视对象;还有一个指针指向创建该文档的文档模板对象;视有一个指向其关联文档的指针,视是一个子窗口,其父窗口是边框窗口(或者文档边框窗口);文档边框窗口有一个指向其当前活动视的指针;文档边框窗口是边框窗口的子窗口。
Windows 管理所有已经打开的窗口,把消息或事件发送给目标窗口。通常,命令消息发送给主边框窗口。
图5-2大略地表示了上述关系:
MFC提供了一些函数来维护这些关系。
表5-2列出了从一个对象得到相关对象的方法。
表5-2 从一个对象得到另一个对象的方法
正在加载中,请稍后...}

我要回帖

更多关于 mfc中的虚函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信