深入浅出话回调上
正文一.什么是回调(Callback)函数 回调函数(Callback Function)是怎样一种函数呢?
函数是用来被调用的,我们调用函数的方法有两种:
l 直接调用:在函数A的函数体里通过书写函数B的函数名来调用之收取拥堵费的最大障碍在于“收费”,使内存中对应函数B的代码得以执行。这里,A称为“主叫函数”(Caller),B称为“被叫函数”(Callee)。
l 间接调用:在函数A的函数体里并不出现函数B的函数名,而是使用指向函数B的函数指针p来使内存中属于函数B的代码片断得以执行——听起来很酷,是吧。
比起直接调用来,间接调用的确麻烦,那为什么还要使用间接调用呢?原因很简单——直接调用把函数名都写进函数体了,经过编译器那么一编译,板上钉钉,A注定调用的是B了,这样的程序只能按照程序员事先设计好的流程执行下去,太呆板了。此时,间接调用的巨大灵活性就显现出来了。想一想,如果p是函数A的一个参数(参数是变量,是变量就可以变吗!),那么程序的最终用户完全可以通过操作来改变p的指向——这样,A在通过p调用函数的时候就有机会调用到不同的函数,这样程序的实用性和扩展性就强多了。
如果你能明白上面一段话,OK,那么你已经明白回调函数的75%了——因为被间接调用的函数就是回调函数本身,而间接调用是使用回调函数的“第二步”。
“第一步”又是什么呢?让我们仔细想想,A通过p来调用B(或者调用其它函数),p是一个指针——空指针是不能用的!所以,“第一步”就是声明函数指针变量p并且为它赋值——即将p绑定到一个将被间接调用的函数上。
这样,使用回调函数的完整流程就成了这样:
声明函数指针p à 向函数指针p赋值,使之指向函数B à 把p作为参数传给函数A à 函数A通过指针p调用函数B à 函数B的函数体得以执行。
现在一个令人迷惑的问题摆在眼前了——Callback这个词里有一个back,上面这个过程哪里体现出“回”了呢?实际上是这样的:
函数指针p的来路我们不必去追究——它可能是一个全局变量(大部分情况下是这样),也可能是一个临时变量——对p赋值却是一件着实重要的事情,除了在声明它的时候对它初始化,我们只能在某个函数中去执行对p进行赋值的操作了,我们姑且管这个函数叫K。现在闭上眼睛跟我想读着有些拗口,函数K和函数B处在同一个程序模块(比如一个类或者一个DLL)Module_1中,而函数A处在另一个程序模块Modules_2中,把p想象成一条短信。Module_1发短信p给Module_2,短信p的内容是“函数B的内存地址是0XFFEE”,Module_2收到短信后使用这个地址,结果相当于给Module_2“回了个”。所以,一个“回”字,是站在包含了函数K和B的模块Module_1的角度上去看而得出的结果。
现在想想,台湾的IT同仁保留了Callback一词的英语词条直译,就叫“回呼函数”,真是高明。将Callback特化成计算机专业术语、叫做“回调”真不知道是谁的主意:p
二.小牛试刀 咱们老规矩,贴段代码放在这儿供大家拍砖玩儿。
//----------------------------------------------
// 水之真谛
//
//----------------------------------------------
#include iostream
// 声明一个函数指针,返回值为void,接受一个整形参数
typedef void (*FunctionPointer)( );
void Function_A( );
void Function_B( FunctionPointer );
void Function_C( );
void Function_D( );
// 回调过程的原始发起者
void Function_A( )
{
FunctionPointer p = 0;
std::cout "Please give me a choice, 1 or 2." std::endl;
int choice = 0;
std::cin choice;
if(choice == 1)
p = Function_C;
else if(choice ==2 )
p = Function_D;
else
return;
Function_B( p );
}
// 回调过程中的“主叫函数”,Caller
void Function_B(FunctionPointer p)
{
p( );
}
// 回调过程中的“被叫函数”,Callee
// 也就是回调函数本身,回调函数-1
void Function_C( )
{
std::cout "I am Function_C. Merry Christmas!" std::endl;
}
//回调函数-2
void Function_D( )
{
std::cout "I am Function_D. Happy New Year!" std::endl;
}
int main(int argc, char *argv[])
{
Function_A( );
return 0;
}
查看本文来源
舟山治疗白癜风好的医院首荟通便胶囊怎么样宝宝不爱吃饭的原因- 06月21日悬疑灵异给狗狗洗脸有哪些学问图位置
- 06月21日悬疑灵异给狗狗怎么刷牙位置
- 06月21日悬疑灵异给猫咪洗澡步骤有哪些位置
- 06月21日悬疑灵异夏季饲养斗牛犬要注意哪些问题位置
- 06月21日悬疑灵异给猫咪拍照的技巧位置
- 06月20日悬疑灵异狼青属于什么犬全面解析狼青犬位置
- 06月20日悬疑灵异猎狐犬到底有没有汗腺呢位置
- 06月20日悬疑灵异猫为什么不用猫砂怎么教猫用猫砂位置
- 06月20日悬疑灵异猫不吃东西怎么办猫咪不吃东西的解决办法位置
- 06月20日悬疑灵异猫咪上厕所困难是什么病位置
- 06月19日悬疑灵异狗狗日常护理口臭的狗狗位置
- 06月19日悬疑灵异狗狗断尾品种大全及断尾长度速查表位置