C++ 回调函数详解
概念
一个程序运行时,所有和运行相关的资源都需要被加载到内存中,如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。
使用函数指针实现函数调用
1 #include <iostream> 2 3 typedef void (*PINVOKE)(const char *str); 4 5 void Invoke(const char *str) 6 { 7 std::cout << str << std::endl; 8 } 9 10 int main() 11 { 12 PINVOKE fp = Invoke; 13 fp("Hello world."); 14 15 return 0; 16 }
说明
函数指针与函数声明的唯一区别就是用指针名(*fp
)代替了函数名Invoke
,然后进行赋值fp = Invoke
就可以进行函数指针的调用了。声明函数指针时要求函数返回值类型、参数个数、参数类型等与已定义函数保持一致。注意,函数指针必须用括号括起来 void (*fp)(char *s)
。
2、回调函数
概念
声明并定义一个函数A
,然后把函数A
的指针作为参数传入其他的函数(或系统)中,其他的函数(或系统)在运行时通过函数指针调用函数A
,这就是所谓的回调函数。简单来说:回调函数就是一个通过函数指针调用的函数。
示例
1 #include <iostream> 2 3 typedef void (*CALLBACKFUN)(const char *str); 4 5 void PrintText(CALLBACKFUN fp, const char *str) 6 { 7 fp(str); 8 } 9 10 void Invoke(const char *str) 11 { 12 std::cout << str << std::endl; 13 } 14 15 int main() 16 { 17 PrintText(Invoke, "Hello world."); 18 return 0; 19 }
类成员函数作为回调函数
回调函数是基于C
- Windows SDK
的技术,不是针对C++
的,程序员可以将一个C
函数直接作为回调函数,但是如果试图直接使用C++
的成员函数作为回调函数将发生错误,因为普通的C++
成员函数都隐含一个传递函数作为参数,即this
指针,C++
通过向其他成员函数传递一个指向自身的指针来实现程序函数访问C++
数据成员。所以实现类成员函数作为回调函数有两种途径:1、不使用成员函数(使用友元操作符friend
的C
函数访问类的数据成员);2、使用静态成员函数。
例如:
1 #include <iostream> 2 3 class CPrintString 4 { 5 public: 6 void PrintText(const char *str) 7 { 8 std::cout << str << std::endl; 9 } 10 11 static void SPrintText(void *pPs, const char *str) 12 { 13 CPrintString *pThis = static_cast<CPrintString *>(pPs); 14 if(NULL == pPs) 15 { 16 return; 17 } 18 pThis->PrintText(str); 19 } 20 }; 21 22 typedef void (*PRINTTEXT)(void *pPs, const char *str); 23 24 void CallBackFun(void *pPs, const char *str, PRINTTEXT fp) 25 { 26 fp(pPs, str); 27 } 28 29 int main() 30 { 31 CPrintString obj; 32 CallBackFun((void *)&obj, "Hello world.", CPrintString::SPrintText); 33 34 return 0; 35 }
3、为什么使用回调函数
一般情况下,回调函数能被普通函数替换,但回调函数最重要的作用是解耦,在这一特点上普通函数代替不了回调函数。
例子:
1 #include <stdio.h> 2 #include <softwareLib.h> // 包含 Library Function 所在读得 Software library 库的头文件 3 4 int Callback() // Callback Function 5 { 6 // TODO 7 return 0; 8 } 9 10 int main() // Main program 11 { 12 // TODO 13 Library(Callback); 14 // TODO 15 return 0; 16 }
乍一看,回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,一般情况下库函数对开发人员并不可见,库函数的实现一般不会被修改,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那就只能通过传入不同的回调函数了,这在企业开发中非常常见。