回调函数

回调函数

回调 函数就是一个通过 函数指针调用的 函数。如果你把函数的 指针(地址)作为 参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调 函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

目录

1机制

2意义

3实现

1机制编辑

⑴定义一个回调 函数
⑵提供 函数实现的一方在初始化的时候,将回调函数的函数 指针注册给调用者;
⑶当特定的事件或条件发生的时候,调用者使用 函数指针调用回调函数对事件进行处理。

2意义编辑

因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的 被调用函数。简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
想知道回调 函数在实际中有什么作用?先假设有这样一种情况:我们要编写一个库,它提供了某些 排序算法的实现(如 冒泡排序快速排序shell排序、shake排序等等),为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,能让库可用于多种 数据类型(int、float、string),此时,该怎么办呢?可以使用 函数指针,并进行回调。
回调可用于通知机制。例如,有时要在A程序中设置一个 计时器,每到一定时间,A程序会得到相应的通知,但通知机制的实现者对A程序一无所知。那么,就需一个具有特定原型的 函数指针进行回调,通知A程序事件已经发生。实际上, API使用一个回调 函数SetTimer()来通知 计时器。如果没有提供回调 函数,它还会把一个消息发往程序的 消息队列
另一个使用回调机制的API 函数是EnumWindow(),它 枚举屏幕上所有的顶层窗口,每个窗口都可以通过它调用另一个程序提供的函数,并传递窗口的处理程序。例如:如果被调用者返回一个值,就继续进行 迭代;否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。
不管怎么说,回调 函数是继承自C语言的。在C++中,应只在与C代码建立接口或与已有的回调接口打交道时,才使用回调 函数。除了上述情况,在C++中应使用虚拟方法或 仿函数(functor),而不是回调函数。

3实现编辑

代码实现
下面创建了一个sort.dll的 动态链接库,它导出了一个名为CompareFunction的类型--
1
typedef int (__stdcall *CompareFunction)( const byte*, const byte*)
,它就是回调 函数的类型。另外,它也导出了两个方法:Bubblesort()和Quicksort(),这两个方法原型相同,但实现了不同的 排序算法
1
2
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()就稍微复杂一点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void DLLDIR __stdcall Bubblesort(byte* array, int size, int elem_size,CompareFunction cmpFunc)
{
     for ( int i=0; i < size; i++)
     {
         for ( int j=0; j < size-i-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 [] temp;
             }
         }
     }
}
注意:因为实现中使用了memcpy(),所以 函数在使用的 数据类型方面,会有所局限。
对使用者来说,必须有一个回调函数,其地址要传递给Bubblesort()函数。下面有二个简单的示例,一个比较两个整数,而另一个比较两个字符串:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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(),同时还传递了一个指向回调 函数的指针。(使用byte类型需包含头文件windows.h,或
1
typedef unsigned char byte;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main( int argc, char * argv[])
{
     int i;
     int array[] = {5432,4321,3210,2109,1098};
     cout << "Before sorting ints with Bubblesort\n" ;
     for (i=0; i < 5; i++)
         cout << array[i]<< '\n' ;
     Bubblesort((byte*)array,5, sizeof (array[0]),&CompareInts);
     cout << "After the sorting\n" ;
     for (i=0; i < 5; i++)
         cout << array[i]<< '\n' ;
     const char str[5][10] = { "estella" , "danielle" , "crissy" , "bo" , "angie" };
     cout << "Before sorting strings with Quicksort\n" ;
     for (i=0; i < 5; i++)
         cout << str[i]<< '\n' ;
     Quicksort((byte*)str,5,10,&CompareStrings);
     cout << "After the sorting\n" ;
     for (i=0; i < 5; i++)
         cout << str[i]<< '\n' ;
     return 0;
}
如果想进行降序排序(大元素在先),就只需修改回调 函数的代码,或使用另一个回调函数,这样编程起来灵活性就比较大了。
调用约定
上面的代码中,可在 函数原型中找到__stdcall,因为它以双下划线打头,所以它是一个特定于 编译器的扩展,说到底也就是 微软的实现。任何支持开发基于Win32的程序都必须支持这个扩展或其等价物。以__stdcall标识的 函数使用了标准调用约定,为什么叫标准约定呢,因为所有的Win32 API(除了个别接受可变参数的除外)都使用它。标准调用约定的 函数在它们返回到调用者之前,都会从 堆栈中移除掉参数,这也是Pascal的标准约定。但在C/C++中,调用约定是调用者负责清理 堆栈,而不是 被调用函数;为强制函数使用C/C++调用约定,可使用__cdecl。另外,可变参数 函数也使用C/C++调用约定。
Windows 操作系统采用了标准调用约定(Pascal约定),因为其可减小代码的体积。这点对早期的Windows来说非常重要,因为那时它运行在只有640KB内存的电脑上。
如果你不喜欢__stdcall,还可以使用CALLBACK宏,它定义在windef.h中:
1
2
#define CALLBACK __stdcallor
#define CALLBACK PASCAL //而PASCAL在此被#defined成__stdcall
作为回调 函数的C++方法
因为平时很可能会使用到C++编写代码,也许会想到把回调 函数写成类中的一个方法,但先来看看以下的代码:
1
2
3
4
5
6
class CCallbackTester
{
public :
     int CALLBACK CompareInts( const byte* velem1, const byte* velem2 );
};
Bubblesort( (byte*)array,5, sizeof (array[0]), &CCallbackTester::CompareInts );
如果使用 微软编译器,将会得到下面这个编译错误:
1
error 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
这是因为非 静态成员 函数有一个额外的参数:this 指针,这将迫使你在成员函数前面加上static。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值