什么是回调函数
回调函数是一种被主动调用并用来进行相应数据推送的一种函数形式,通常回调函数的编写者只负责进行回调函数的编程实现,不进行主动执行。
回调函数的实现方式
回调函数通过函数指针的形式实现。函数指针是一个指向特定函数的指针,函数的类型由其参数以及返回类型共同决定,与函数的具体名称没有什么关系。
typedef int(*pFunc)(int,int),在该指针前面的int为函数的返回类型,(int,int)为函数的参数,由两个int类型的参数组成、
回调函数的实现
#include <iostream>
using namespace std;
typedef int(*pFunc)(int, int);//首先定义一个函数指针,形式为回调函数的具体返回类型以及参数组成
/*回调函数的调用函数,通过该函数实现对回调函数的调用,
回调函数在该函数的调用方式是该函数的参数形式,并且对以
上定义的函数指针创建一个实例作为入参*/
int process(int a, int b, pFunc Callback)
{
cout << "callback start" << endl;
Callback(a,b);//调用回调函数,将所需参数传入
return 0;
}
/*回调函数的真正实现,返回类型和参数与定义的函数指针形式保持一致*/
int Add(int a, int b)
{
cout << "enter callback" << endl;
return a + b;
}
/*主函数*/
int main()
{
cout << "main function" << endl;
process(1, 2, Add)//回调函数的调用;
system("pause");
return 0;
}
回调函数的应用场景
为什么要使用?因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些的实现,如、快速排序、shell排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种(int、float、string),此时,该怎么办呢?可以使用,并进行回调。
事件驱动机制
回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer() API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序。
另一个使用回调机制的是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。
事件驱动机制
为了简单说明该机制,我们假定有两个类,类A与类B。该模式的工作机制如下:
1.类A提供一个回调函数F,该回调函数执行根据不同的参数,执行不同的动作;
2.类A在初始化类B时,传入回调函数F的函数指针pF;
3.类B根据需要在不同的情况下调用回调函数指针pF,这样就实现了类B来驱动类A,类A来响应类B的动作。
通信协议的“推”模式
在我们实际工作中,经常会遇到数据通信的问题。总体来说,两个对象要实现数据通信,有以下两种方式:
1.“拉”模式
在该模式下,假定对象A要从对象B中获取实时数据信息,“拉”模式的工作机制如下:
(1)对象A开启一个线程,该线程执行一个循环,每隔一定时间间隔,向对象B发出数据请求;
(2)对象B一旦有新的信息,就利用对象B的数据请求,将信息发送给对象B。
注意:该模式的主要问题是需要维护一个循环线程。时间间隔太长会导致,通信的实时性下降;时间间隔太短,会导致CPU浪费太多。
2.“推”模式
在该模式下,假定对象A要从对象B中获取实时数据信息,“推”模式的工作机制如下:
(1)对象A在调用对象B时,向其传递一个回调函数;
(2)对象B一旦有新的信息,就调用对象A传递过来的函数指针,将最新的信息发送给对象A。
注意:该模式完美解决了“拉“模式产生的问题,不但保证了数据传输的实时性,而且降低了无用的CPU消耗。一般的通信协议,建议采用”推“模式。
注意:也不可以盲目使用回调函数,因为回调函数的使用会增加系统的开销