转:C++回调函数

107 篇文章 1 订阅

C/C++之回调函数

     今天讨论下C/C++中的回调函数。

     在理解“回调函数”之前,首先讨论下函数指针的概念。

函数指针

(1)概念:指针是一个变量,是用来指向内存地址的。一个程序运行时,所有和运行相关的物件都是需要加载到内存中,这就决定了程序运行时的任何物件都可以用指针来指向它。函数是存放在内存代码区域内的,它们同样有地址,因此同样可以用指针来存取函数,把这种指向函数入口地址的指针称为函数指针。

(2)先来看一个Hello World程序:

int main(int argc,char* argv[])
{
    printf("Hello World!\n");
    return 0;
}

       然后,采用函数调用的形式来实现:

复制代码

void Invoke(char* s);

int main(int argc,char* argv[])
{
    Invoke("Hello World!\n");
    return 0;
}

void Invoke(char* s)
{
    printf(s);
}

复制代码

      用函数指针的方式来实现:

复制代码

void Invoke(char* s);

int main()
{
    void (*fp)(char* s);    //声明一个函数指针(fp)        
    fp=Invoke;              //将Invoke函数的入口地址赋值给fp
    fp("Hello World!\n");   //函数指针fp实现函数调用
    return 0;
}

void Invoke(char* s)
{
    printf(s);
}

复制代码

      由上知道:函数指针函数的声明之间唯一区别就是,用指针名(*fp)代替了函数名Invoke,这样这声明了一个函数指针,然后进行赋值fp=Invoke就可以进行函数指针的调用了。声明函数指针时,只要函数返回值类型、参数个数、参数类型等保持一致,就可以声明一个函数指针了。注意,函数指针必须用括号括起来 void (*fp)(char* s)。

     实际中,为了方便,通常用宏定义的方式来声明函数指针,实现程序如下:

复制代码

typedef void (*FP)(char* s);
void Invoke(char* s);

int main(int argc,char* argv[])
{
    FP fp;      //通常是用宏FP来声明一个函数指针fp
    fp=Invoke;
    fp("Hello World!\n");
    return 0;
}

void Invoke(char* s)
{
    printf(s);
}

复制代码

 

函数指针数组

      下面用程序对函数指针数组来个大致了解:

复制代码

#include <iostream>
#include <string>
using namespace std;

typedef void (*FP)(char* s);
void f1(char* s){cout<<s;}
void f2(char* s){cout<<s;}
void f3(char* s){cout<<s;}

int main(int argc,char* argv[])
{
    void* a[]={f1,f2,f3};   //定义了指针数组,这里a是一个普通指针
    a[0]("Hello World!\n"); //编译错误,指针数组不能用下标的方式来调用函数

    FP f[]={f1,f2,f3};      //定义一个函数指针的数组,这里的f是一个函数指针
    f[0]("Hello World!\n"); //正确,函数指针的数组进行下标操作可以进行函数的间接调用
    
    return 0;
}

复制代码

 

回调函数

(1)概念:回调函数,顾名思义,就是使用者自己定义一个函数,使用者自己实现这个函数的程序内容,然后把这个函数作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。

(2)标准Hello World程序:

int main(int argc,char* argv[])
{
    printf("Hello World!\n");
    return 0;
}

      将它修改成函数回调样式:

复制代码

//定义回调函数
void PrintfText() 
{
    printf("Hello World!\n");
}

//定义实现回调函数的"调用函数"
void CallPrintfText(void (*callfuct)())
{
    callfuct();
}

//在main函数中实现函数回调
int main(int argc,char* argv[])
{
    CallPrintfText(PrintfText);
    return 0;
}

复制代码

      修改成带参的回调样式:

复制代码

//定义带参回调函数
void PrintfText(char* s) 
{
    printf(s);
}

//定义实现带参回调函数的"调用函数"
void CallPrintfText(void (*callfuct)(char*),char* s)
{
    callfuct(s);
}

//在main函数中实现带参的函数回调
int main(int argc,char* argv[])
{
    CallPrintfText(PrintfText,"Hello World!\n");
    return 0;
}

复制代码

      至此,对回调函数应该有了一个大致的了解。

 

https://blog.csdn.net/bingbaobei/article/details/51782990

转载来自http://blog.163.com/ssou_1985/blog/static/2953203620123361340352/

看了人家的例子,比看那么多定义好多了。一看就明白了。好人啊!老外把国人玩的不是人了。国人还自己玩自己。非把一个简单的东西复杂化。叫那么难理解!!完啦,窝里斗的典型!!!!!!!!

回调函数:我的理解。假设   A是回调函数,B是调用者,B参数里一个是指向A的函数指针,即回调A,同时另外的参数传递给A作为参数。A可以是多个函数的统一指向,只要函数参数个数相同即可。

WINDOWS回调函数:永远不会被程序中的其他函数或子程序调用。只能由操作系统调用。因此,windows可以通过传递不同参数给回调函数达到和程序沟通的目的。 

那么:B调用A,A也有参数,有参数就要赋值才行。所以B函数内部给A参数赋值。B调用A,A又利用了B给的参数。

A就是回调函数。B就是调用者。

int* func(int params, ...); //这就是指针函数

当一个函数的返回值是一个指针时,该函数就是一个指针函数。

函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。

同比指针变量指向数组首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。

 回调函数参数可以空或者定义成void类型。方便不同类型的数据传入。通用性强。

 int sort_function( const void *a, const void *b);   //参数类型是void 两个参数。传入参数可以任意类型。更通用。 回调函数的实现,参数也用void。类型由调用者传的参数决定。

#include<stdio.h>

// 方法指针的格式为:int (*ptr)(char *p) 即:返回值(指针名)(参数列表)
typedef int (*CallBackFun)(char *p); // 为回调函数命名,类型命名为 CallBackFun,参数为char *p 。

//下面是两个被调用者,就是回调函数的实现。回调函数是他们的统一格式。

int Afun(char *p)

{ // 方法 Afun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun
printf("Afun 回调打印出字符%s!\n", p);
return 0;
}

int Cfun(char *p) { // 方法 Bfun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun
printf("Cfun 回调打印:%s, Nice to meet you!\n", p);
return 0;
}

/*********************************************************************/

//下面的这些是调用者:有几种方式,看你喜欢哪种

int call(CallBackFun pCallBack, char *p)

 {    // 执行回调函数,方式一:通过命名方式
printf("call 直接打印出字符%s!\n", p);
pCallBack(p);
return 0;

}

/*********************************************************************/
int call2(char *p, int (*ptr)())

{     // 执行回调函数,方式二:直接通过方法指针
printf("==============\n", p); 
(*ptr)(p); 
}

/*********************************************************************/

int call3(char *p, CallBackFun pCallBack)

{      // 执行回调函数,方式一:通过命名方式
printf("--------------\n", p);
pCallBack(p); 
}

/*********************************************************************/

int main()

 {   

char *p = "hello";

call(Afun, p);   //调用Afun回调函数,传递参数*P给Afun函数

 call(Cfun, p); //调用Cfun回调函数,传递参数*P给Afun函数

call2(p, Afun); //调用Afun回调函数,传递参数*P给Afun函数
    call2(p, Cfun); //调用Cfun回调函数,传递参数*P给Afun函数

call3(p, Afun);  //调用Afun回调函数,传递参数*P给Afun函数
    call3(p, Cfun); //调用Cfun回调函数,传递参数*P给Afun函数

// int i = getchar();
// printf("Input: %c \n", i);

return 0;
}

/*********************************************************************/

Linux下的编译、输出:

[jhliu@localhost src]$ gcc -o func_callback func_callback.c
[jhliu@localhost src]$ ./func_callback
call 直接打印出字符hello!
Afun 回调打印出字符hello!

==============
call 直接打印出字符hello!
Cfun 回调打印:hello, Nice to meet you!
==============
Afun 回调打印出字符hello!
==============
Cfun 回调打印:hello, Nice to meet you!
--------------
Afun 回调打印出字符hello!
--------------
Cfun 回调打印:hello, Nice to meet you!
[jhliu@localhost src]$

 

 

成为C++高手之回调函数

置顶 2016年05月19日 05:03:51 牛搞 阅读数:11211更多

所属专栏: 成为C++高手

上一节的排序函数只能正向排序,那我们需要反向排序怎么办?可以增加一个参数嘛,传入TRUE,就表示要正向排,传入FALSE,就表示要反向排。要改变排序方向,只需改变两项比较时是用大于号还是小于号即可。但是这里有更高级的玩法,即传入的参数不是一个BOOL型值,而是一个函数指针。

函数名本身就是一个指针,调试时看函数名,其值就是一个地址。但在逻辑上不能把函数名叫函数指针,必须另创建一个指针指向这个函数,通过这个指针调用函数才叫回调。

函数指指与变量指针没实质区别,都是保存一个表示内存位置的整数,但编译器必须能区分指针的类型才行。所以函数指针在定义时必须指定其类型。所以要为函数定义类型。函数类型是由函数返回值类型、参数数量、参数类型决定,与函数的实现无关。就是长一个样的函数都是同一类型。

我们这里增加一个参数,传入的函数指针指向一个比较函数,仅代替“arr[j]>arr[j+1]”部分。比较函数必须有两个参数指向要比较的元素的序号,返回必须是BOOL型数据,表示比较结果。if语句跟据比较函数返回值决定是否交换位置。

#include <stdio.h>
//定义比较函数类型
typedef int (*CompareFunc)(int *,int ,int );
//前置声明排序函数,第三个参数是比较函数类型
void sort(int * ,int ,CompareFunc);

//返回非0数表示比较成功,返回0表示失败
int compare1(int *arr,int a,int b){
    return arr[a]>arr[b];
}
int compare2(int *arr,int a,int b){
    return arr[a]<arr[b];
}

int main(int argc, const char * argv[]) {

    int arr[]={22,33,45,1,2,7,66,554,23424234,22};

    //排序,第三个参数可以传入两个比较函数之一
    sort(arr,sizeof(arr)/sizeof(int),compare1);

    //打印排序后的数组
    for(int i=0;i<sizeof(arr)/sizeof(int);i++){
        printf("%d ",arr[i]);
    }

    return 0;
}

//第一个参数是要排序的数组,第二个是数组两元素的数量
void sort(int *arr ,int count,CompareFunc compare_func){
    int i;
    //外层循环是倒着来的
    for(i=count-1;i>0;i--){
        int j;
        for(j=0;j<i;j++){
            //调用比较函数进行比较
            if(compare_func(arr,j,i)){
                int tmp = arr[j];
                arr[j]=arr[i];
                arr[i]=tmp;
            }
        }
    }
}

改用回调函数有什么好处呢?此处并不明显,甚至更麻烦。但试想这种情况:如果我们数组中不是简单数值,而是一个结构。比如扑克牌,一张牌至少有两个属性:花色和点数,对应到结构中就是两个成员变量。当牌发到玩家手中后,是乱序的,玩家需要整理好其顺序。不同的游戏有不同的排序规则,那我们可以为每种游戏规则都写一个不同的排序函数,或者这样做:排序函数固定,只为每种不同的游戏写不同的比较函数。这增强了排序代码的重用性。
上一篇:成为C++高手之for循环

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值