回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调函数实现
- 定义一个回调函数;
- 提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
- 当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
实例代码:
#include <iostream>
using namespace std;
typedef void (*CALLBACK)(int a,int b);
class base
{
private:
int m;
int n;
static CALLBACK func;
public:
void registercallback(CALLBACK fun,int k,int j);
void callcallback();
};
CALLBACK base::func=NULL;
void base::registercallback(CALLBACK fun,int k,int j)
{
func=fun;
m=k;
n=j;
}
void base::callcallback()
{
base::func(m,n);
}
void seiya(int a,int b)
{
cout<<a<<endl<<b<<endl;
cout<<"this is seiya callback function"<<endl;
}
void zilong(int a,int b)
{
cout<<a<<endl<<b<<endl;
cout<<"this is zilong callback function"<<endl;
}
void main(void)
{
base ba;
ba.registercallback(seiya,2,3);
ba.callcallback();
ba.registercallback(zilong,5,6);
ba.callcallback();
}
C语言中的回调
函数指针
回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此,要实现回调,必须首先定义函数指针,请看下面的例子:
void Func(char *s);// 函数原型
void (*pFunc) (char *);//函数指针
可以看出,函数的定义和函数指针的定义非常类似。
一般的化,为了简化函数指针类型的变量定义,提高程序的可读性,我们需要把函数指针类型自定义一下。
typedef void(*pcb)(char *);
回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数。
被调函数的例子:
void GetCallBack(pcb callback)
{
/*do something*/
}
//用户在调用上面的函数时,需要自己实现一个pcb类型的回调函数:
void fCallback(char *s)
{
/* do something */
}
//然后,就可以直接把fCallback当作一个变量传递给GetCallBack,
GetCallBack(fCallback);
如果赋了不同的值给该参数,那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。
参数传递规则
到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。
将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:
// 被调用函数是以int为参数,以int为返回值
__stdcall int callee(int);
// 调用函数以函数指针为参数
void caller( __cdecl int(*ptr)(int));
// 在p中企图存储被调用函数地址的非法操作
__cdecl int(*p)(int) = callee; // 出错
指针p和callee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列
回调函数例子1:
C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。
快速排序函数原型:
void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
二分搜索函数原型:
void *bsearch(const void *key, const void *base, size_t nelem,
size_t width, int (_USERENTRY *fcmp)(const void *, const void
其中fcmp就是一个回调函数的变量。
下面给出一个具体的例子:
#include <stdio.h>
#include <stdlib.h>
int sort_function( const void *a, const void *b);
int list[5] = { 54, 21, 11, 67, 22 };
int main(void)
{
int x;
qsort((void *)list, 5, sizeof(list[0]), sort_function);
for (x = 0; x < 5; x++)
printf("%i\n", list[x]);
return 0;
}
int sort_function( const void *a, const void *b)
{
return *(int*)a-*(int*)b;
}
测试代码2
#include "stdafx.h"
#include "windows.h"
#include <iostream>
using namespace std;
//回调函数指针
typedef int(WINAPI* WNDCALLBACK)(int*,int*);
//回调函数
int __stdcall Call(int *a,int *b);
//定义类
class A
{
public:
//比较两个数字大小
int Compare(int m,int n,WNDCALLBACK function)
{
int *a=&m;
int *b=&n;
return (*function)(a,b);
}
};
int main(int argc, char* argv[])
{
int a=10;
int b=6;
A m_a;
cout<<m_a.Compare(a,b,Call)<<endl;
return 0;
}
//回调函数
int CALLBACK Call(int *a,int *b)
{
return *a>(*b)?(*a):(*b);
}
回调函数例子2:
回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。
CODE:
typedef void (*f1) ();// 为函数指针声明类型定义
void (*p) (); //p是指向某函数的指针
void func1()
{
printf("From func1(), Hello World!\n"); /* do something */
}
void caller(void(*ptrfunc1)())
{
ptrfunc1(); /* 调用ptr指向的函数 */
}
//typedef bool (*f2) (int *);// 为函数指针声明类型定义
//bool (*q) (int *); //p是指向某函数的指针
bool func2(int* t_i)
{
/* do something */
printf("From func2() = %d, Hello World!\n", (*t_i)++);
return true;
}
void caller2(bool (*ptrfunc2)(int *), int * i)
{
ptrfunc2(i); /* 调用ptr指向的函数 */
}
int main(int argc, char* argv[])
{
printf("From main(), Hello World!\n");
printf("\n");
//无参数调用
p = func1; /* 传递函数地址地址 */
caller(p); /* 传递函数地址到调用者 */
//有参数调用
int i = 0;
for (int j = 0; j < 10; j++)
{
caller2(func2, &i); //* 传递函数地址到调用者 */
}
//有参数调用第二次
i = 0;
//q = func2; /* 传递函数地址地址 */
//caller2(q, &i); /* 传递函数地址到调用者 */
printf("\n");
printf("From main(), Hello World!\n");
getchar();
return 0;
}
回调函数例子3
下面是自己写的一个简单的回调函数,相比其他的那些复杂的代码,这个更容易理解:
#include<stdio.h>
#include<stdlib.h>
void perfect(int n)
{
int i=1;
int count=0;
for(i=1;i<n;i++)
{
if(0==n%i)
{
count+=i;
}
}
if(count==n)
printf("%d是完数\n",n);
else printf("%d不是完数\n",n);
}
void myCallback(void (*perfect)(int ),int n)
{
perfect(n);
}
int main()
{
int n;
printf("请输入一个正整数\n");
scanf("%d",&n);
myCallback(perfect,n);
return 0;
}