做了《Linux C一站式编程》中的一道习题,通过回调函数实现了对多种数据类型的插入排序。
如下例,在实现选择排序函数insertion_sort( )的时候,通过指向比较函数的指针cmp来比较大小。待比较的数据的数据类型在insertion_sort( )实现的时候是不知道的,在insertion_sort( )在main.cpp中被调用的时候才知道,所以cmp具体指向的比较函数(如cmp_int, cmp_float, cmp_char)在insertion_sort( )被调用的地方(main.cpp)才得到具体实现。
下面是头文件
/* generic_sort.h */
/*
* 目标: 实现回调函数版本的插入排序
* 1 对data[]的处理方法。data是一个数组,每个元素是void*类型,所以data数组名字是指向void*的指针,是一个指向指针的指针。
*
*/
#ifndef GENERIC_SORT_H
#define GENERIC_SORT_
typedef int (*cmp_t)(void *, void *);
extern void *insertion_sort(void *data[], int num, cmp_t cmp);
#endif
下面是实现文件
/*generics_sort.cpp */
//目标: 实现回调函数版本的插入排序
#include "generic_sort.h"
void * insertion_sort(void *data[], int num, cmp_t cmp)
//注意第1个参数是一个指向指针的指针,data[]表示说明是数组类型,所以是指针。数组中的元素是void*型,所以是指向指针的指针。
{
for ( int j = 1; j < num; j++)
{
int m = j-1;
void* key = data[j];
while( m>=0 &&(cmp(key,data[m])==-1) ) {
data[m+1] = data[m];
m--;
}
data[m+1]=key;
}
return data[0];
}
下面是main.cpp文件
/* main.cpp */
//目标:实现回调函数版本的插入排序
#include <iostream>
#include <stdio.h>
#include "generic_sort.h"
void print_num_int_p(int** arr, int len){
for(int i=0;i<len;i++)
std::cout<<*arr[i]<<"\t";
std::cout<<std::endl;
return;
}
void print_num_double_p(double** arr, int len){
for(int i=0;i<len;i++)
std::cout<<*arr[i]<<"\t";
std::cout<<std::endl;
return;
}
void print_num_char_p(char** arr, int len){
for(int i=0;i<len;i++)
std::cout<<*arr[i]<<"\t";
std::cout<<std::endl;
return;
}
int cmp_int(void *a, void *b)
{
if( (*(int *)a) > (*(int *)b) )
return 1;
else if( (*(int *)a) == (*(int *)b) )
return 0;
else
return -1;
}
int cmp_double(void *a, void *b)
{
if( (*(double *)a) > (*(double *)b) )
return 1;
else if( (*(double *)a) == (*(double *)b) )
return 0;
else
return -1;
}
int cmp_char(void *a, void *b)
{
if( (*(char*)a) > (*(char *)b) )
return 1;
else if( (*(char*)a) == (*(char *)b) )
return 0;
else
return -1;
}
int main(void){
int list_a[4] = {3,7,5,1};
double list_b[4] = {0.3,7.2,5.6,1.9};
char list_c[4] = {'b','c','B','e',};
int *plist_a[4] = {&list_a[0], &list_a[1], &list_a[2], &list_a[3]};
double *plist_b[4] = {&list_b[0], &list_b[1], &list_b[2], &list_b[3]};
char *plist_c[4] = {&list_c[0], &list_c[1], &list_c[2], &list_c[3]};
print_num_int_p(plist_a,4);
insertion_sort((void**)plist_a,4,cmp_int);
print_num_int_p(plist_a,4);
print_num_double_p(plist_b,4);
insertion_sort((void**)plist_b,4,cmp_double);
print_num_double_p(plist_b,4);
print_num_char_p(plist_c,4);
insertion_sort((void**)plist_c,4,cmp_char);
print_num_char_p(plist_c,4);
int a = getchar();
return 0;
}
下面是输出结果
简单地讲,回调函数就是具有类似下面这样的功能的函数。
在function_a实现的时候, 通过函数指针指向了了函数function_b,
对于function_a而言,它并不知道function_b内部具体是怎样实现的,它只知道function_b的输入输出。它会给function_b传入数据,并且根据function_b的输出结果进一步做事情。
对于function_a的调用者而言(如在main.cpp中调用的时候),调用者需要体用具体的function_b的实现。
这样的情况下,function_b就被称作为回调函数。称作为"回调"是因为,在function_a的实现中,只是通过一个指针调用了function_b,function_a并不知道function_b的内部实现。function_b的真正实现是在function_a被调用的地方具体指明的(如在main.cpp中),绕了一个圈子,所以有回调(call back)的意思。
书中还有更多更清楚的讲解和例子。