870920 Menu

SwingCoder之C++备忘录·6

函数回调

假设某个函数的原型为:void doSomething(int x);

可声明该函数的函数指针,语法格式与函数原型基本一致,只是将函数名替换为指针名,并前置“*”:

void (*funcPointer)(int);

声明此函数指针后,所有原型模式为void xxxx(int)的函数,均可将自身的函数名赋值给该指针。即:函数指针与原型函数的返回类型、参数类型、参数个数必须要绝对一致。内部机制:函数名即该函数的函数指针,该指针保存此函数的内存起始地址。

funcPointer = doSomething; // 将某个函数的函数名赋值给类型一致、参数一致的函数指针

函数指针常用于函数回调。这种技术的关键有四:

 理解函数名即该函数的函数指针,比如:func即func()函数的函数指针。

 要回调该函数(假设为func()),还必须有一个调用该回调函数的函数(假设为callbackFunc())。callbackFunc()的参数之一即是需要回调的函数的函数指针,即func(此时,函数的形参即相当于声明函数指针的语句)。往往,callbackFunc()的其它参数,也正是需要回调的函数所需的参数。

 在回调函数callbackFunc()中,直接使用函数指针执行要回调的函数,并提供其所需的参数。

 写回调函数callBackFunc()时,并不知道具体要调用的是哪个函数,因此,该函数的函数指针形参更像是一个模板类型或占位符。程序员调用callBackFunc()时,传入哪个函数的函数名,回调函数内部就自动调用哪个函数。

更直白的理解函数回调:其实就是函数调函数,不同处在于,普通的函数调函数,所调用的函数是特定的,硬编码或系统根据虚函数机制自动调用的。也就是说,A函数中调用B函数,B函数是确定的,那么,如何做到A函数可以调用任意函数呢?而所调用的任意函数并不是编译时确定的,也不基于同类型的虚函数机制。或者说,喂给A函数一个任意函数,A函数就自动调用之,如何喂给A函数这个需要回调的函数?此时,就必须使用函数回调技术了。通常,A函数中调用B函数,将B函数作为A函数的参数,而某个函数要作为另一个函数的参数,必须使用函数指针的形式才行。示例:
void func1(char* message) // 要回调的函数1
{
cout << “func 1: ” << message << endl;
}
void func2(char* message) // 要回调的函数2
{
cout << “func 2: ” << message << endl;
}
/** 回调函数。1参:函数指针,即本函数所回调的函数。2参:1参函数所需的参数 */
void callbackFunc(void (*func)(char*), char* message)
{
func(message);
}
callbackFunc()回调某个函数:
callbackFunc(func1, “11111”);
callbackFunc(func2, “22222”);

上例中,可简化callbackFunc()函数声明中的1参(函数指针),办法是使用typedef类型重定义:
// FuncPtrType即为重定义的类型,该类型代表所有与void xxx(char*)相匹配的函数
typedef void (*FuncPtrType)(char*);

函数指针的类型重定义后,上述回调函数的声明可写为:
void callbackFunc(FuncPtrType thisFunc, char* message);

上述是针对全局函数的函数指针与函数回调,针对类的成员函数的函数指针与函数回调,稍有不同,但原理一致。分两种情况:

1、类的public静态函数。与全局函数的函数指针与函数回调基本一致,不同处:函数名之前要加取地址运算符、类名和作用域运算符:
callBackFunc(&MyClass::staticFunc, staticFunc的参数1, staticFunc的参数2);

2、类的非静态成员函数只能用对象来调用该函数的函数指针,另外必须对函数指针解引用:
void (MyClass::*funcPtr)(char*) = &MyClass::func1; // 声明函数指针
MyClass* obj = new MyClass(); // 声明对象
callBackFunc(obj->*funcPtr, “1111”); // 回调func1()函数

如果不用于回调,则(obj->*funcPtr)(“1111”)完全等同于obj->func1(“1111”)。
上面的obj如声明为栈对象,则“->*”运算符替换为“.*”。

函数回调非常灵活与强悍,在音频编程和GUI编程的消息传递中使用的非常广泛,比如:程序员需要编写类似于函数1的被回调函数,而后由操作系统、底层驱动程序、程序中的其他线程或模块来回调此函数。将音频采样发送到声卡,就是采用函数回调来实现的典型用例。另一个典型用例是基于函数回调的消息传递机制。