案例一:
有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register a callback function)
叫醒(对象,….,叫醒方式);
案例二:
我们对链表的查找,一般会这样写
Node *search_list( Node *node, int const value )
{
while ( NULL != node ){
if ( node->value == value ){
break;
}
node = node->link;
}
return node;
}
(这里不要较真 有人会接的我把int const 改成type 然后再在上面定义type的类型 这样修改就方便了)
但是分析上面的代码我们发现 该程序只能查找int类型的,如果程序突然改变需要使用到char型 则程序就的修改这样很不利于开发,而且也会给增加程序冗余。
最好的方法是我们依然使用这样的代码 但是不去确定value的类型 其类型有第三方(回调函数)来告诉我们具体的类型。
使用回调函数查找:
int compare_int( void const *a, void const *b )
{
if ( *( int * )a == *( int * )b ){
return 0;
}
return 1;
}
Node *search_list(Node *node, void const *value,
int (*compare)(void const *, void const *)) //函数指针
{
while(node != NULL){
if(compare(&node->value, value) == 0) //相等
break;
node = node->link;
}
return node;
}
这样就可以利用回调函数实现上面的问题了。
案例三:
库函数 以及sort函数都是用了回调函数的思想
void qsort(
void *base, //字符串首地址
size_t num, //排序总个数
size_t width, //排序元素的大小
int (__cdecl *compare )(const void *, const void *) //函数指针
);
总结:
回调并不是“你我”两方的互动,而是ABC的三方联动。有了这个清楚的概念,在自己的代码里实现回调时才不容易混淆出错。
另外,回调实际上有两种:阻塞式回调和延迟式回调。两者的区别在于:阻塞式回调里,回调函数的调用一定发生在起始函数返回之前;而延迟式回调里,回调函数的调用有可能是在起始函数返回之后。