回调函数其实和普通函数一样,不同的是普通函数是直接在程序中进行调用,回调函数是通过函数指针将它的地址传递给其它函数,函数执行在其它函数体执行,这个过程就叫做回调。所以,C++回调函数也并非高大上的技术,它的原理无非就是函数指针或者对象的传递。本文就从函数指针开始对回调函数进行说明。
1 函数指针
函数指针是指一个变量,函数对象创建后就会分配一个地址,这个地址可以通过变量进行保存。这个变量就叫做函数地址变量,也可以称之为函数指针。
函数指针的定义方式如下:
int(*p)(int, int);
如上,定义了一个函数指针,它指向一个包含两个整型参数且返回值为整型数值的函数对象。
函数指针在C和C++中被经常使用,使用方式也很简单,具体如下面代码所示:
typedef int (*Ptr)(int,int);
int add(int a,int b){
return (a+b);
}
int main()
{
Ptr pInt = add;
cout<<pInt(3,5)<<endl;
return 0;
}
2 C风格的回调函数
上面的代码是将一个函数地址赋值给了函数指针,下面将函数作为回调函数的参数的方式进行处理,函数处理结果也是一致的。代码如下:
typedef int (*Ptr)(int, int);
int CallBack(Ptr pInt, int a, int b) {
return pInt(a, b);
}
int add(int a, int b) {
return (a + b);
}
int main()
{
cout << CallBack(&add, 3, 5) << endl;
}
代码如上,代码运行结果为:8。
实现是不是很简单,通过回调函数,可以让用户自己定义自己的业务实现,且这种方式在网络通讯中被经常使用,下面在看看一下如果回调函数是类成员函数的时候如何实现。
3 C++风格的回调函数
在C++中,如果回调函数是类成员函数,需要将回调函数定义成为静态。当然也可以使用全局函数,但是这样做就会破坏C++的封装性。
下面的代码就演示了将一个静态成员函数作为回调函数的使用情况。
typedef int (*Ptr)(int,int);
int RegFuncation(Ptr pInt,int a,int b){
return pInt(a,b);
}
class COperMath{
public:
//回调函数
static int add(int a,int b){
return (a+b);
}
//注册函数
void RegFuncationCallBack(){
cout<<RegFuncation(add,3,5)<<endl;
return ;
}
};
int main()
{
COperMath pInst;
pInst.RegFuncationCallBack();
return 0;
}
4 多态类型的回调函数
在前面的代码中,都是使用函数指针的方式进行,下面的代码使用多态的方式实现,为了演示实现的方式,代码比较简单。
class CAniable{
public:
virtual void eat()=0;
};
class CCat:public CAniable{
public:
virtual void eat(){
cout<<"Cat like mouse"<<endl;
}
};
class CDog:public CAniable{
public:
virtual void eat(){
cout<<"Dog like shit"<<endl;
}
};
int main()
{
CAniable *pBase = new CCat();
pBase->eat();
CAniable *pBase1 = new CDog();
pBase1->eat();
delete pBase;
delete pBase1;
return 0;
}
代码运行结果为:
Cat like mouse
Dog like shit
上面的代码通过定义一个纯虚的基类,里面定义了一个纯虚的公共接口,其它类都继承自基类,在使用时就可以将这个类指针传递给回调函数,进而实现回调的功能。
5 通过function和bind实现回调函数功能
function功能很函数指针功能类似,不同的是function可以调用各种对象和函数。function还可以调用lamda表达式。具体如下所示。
typedef function<int(int,int)> func;
int add(int a,int b){
return (a+b);
}
class COperMath{
public:
float Sum(float a,float b){
return(a+b);
}
};
int main()
{
//function包裹add
func f = &add;
cout<<"func="<<f(3,5)<<endl;
//function和bind联合使用
COperMath cMath;
function<float(float,float)> func_bind = bind(&COperMath::Sum,ref(cMath),placeholders::_1,placeholders::_2);
cout<<"func_bind="<<func_bind(6.0,10.0)<<endl;
return 0;
}
代码运行结果为:
func=8
func_bind=16
如上,本文使用了5种方式对回调函数进行实现,在实际项目中,使用回调函数的场景比这里要复杂的多,希望大家在实际使用中能够运用自如。
6 总结
回调函数在实际中有许多作用。假设有这样一种情况:我们要编写一个库,该库实现排序功能,但是又不希望在库里实现排序逻辑,这样就可以使用回调函数让用户自己通过函数指针的方式将排序逻辑传进来进行排序。回调可用于网络编程中,如通过回调函数获取服务端返回的数据信息并进行处理。
- EOF -
图文:龙小
排版:龙小