0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一文详解C/C++回调函数

zzw_111_bit 来源:zzw_111_bit 作者:zzw_111_bit 2023-02-12 09:20 次阅读

C/C++回调函数

首先看一下回调函数的官方解释:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。这段解释比较官方。个人可以简单的理解为:**一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。**如果代码立即被执行就称为同步回调,如果过后再执行,则称之为异步回调。

入门案例

int Callback_1(int a)   ///< 回调函数1
{
    printf("Hello, this is Callback_1: a = %d ", a);
    return 0;
}

int Callback_2(int b)  ///< 回调函数2
{
    printf("Hello, this is Callback_2: b = %d ", b);
    return 0;
}

int Callback_3(int c)   ///< 回调函数3
{
    printf("Hello, this is Callback_3: c = %d ", c);
    return 0;
}

int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的函数指针定义
{
    Callback(x);
}

int main()
{
    Handle(4, Callback_1);
    Handle(5, Callback_2);
    Handle(6, Callback_3);
    return 0;
}

在这个入门案例中,Callback_1、2、3就是回调函数,handle函数的第二个参数就是函数指针,也就是通过函数指针来调用。纯C语言通过函数指针来进行回调函数的调用,C++则可以通过引用、Lambda等多种方式来进行,下面进行具体的介绍。

函数指针

首先函数指针也是一种指针,只不过指向的是函数(C语言中没有对象)。然后通过这个指针就可以调用。

int Func(int x);   /*声明一个函数*/
int (*p) (int x);  /*定义一个函数指针*/
p = Func;          /*将Func函数的首地址赋给指针变量p*/
p = &Func;          /*将Func函数的首地址赋给指针变量p*/

经过上述后,指针变量 p 就指向函数 Func() 代码的首地址了。下面看一个具体的例子。

int Max(int x, int y)  //定义Max函数
{
    if (x > y){
        return x;
    }else{
      return y;
    }
}
int main()
{
  int(*p)(int, int);  //定义一个函数指针
  p = Max;  //把函数Max赋给指针变量p, 使p指向Max函数
  int c= (*p)(1,2);//通过函数指针调用Max函数
  printf("%d",c);  
  return 0;
}

p指向Max函数之后,然后用p调用Max函数,返回两个数中的最大值。特别注意的是,因为函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址。

p = Max可以改成 p = &Max;

c = (*p)(a, b) 可以改成 c = p(a, b)

所以函数指针的通常写法是

函数返回值类型 (* 指针变量名) (函数参数列表);

在这里指针变量名也可以叫做函数名,

但是通常可以用typedef进行描述

typedef 函数返回值类型 (* 指针变量名) (函数参数列表);

最后需要注意的是,指向函数的指针变量没有 ++ 和 -- 运算。

C++类的静态函数作为回调函数

前面函数指针的方式作为回调函数的一种方式,可以同时用于C和C++,下面介绍另外的一些方式,因为C++引入了对象的概念,可以使用类的成员和静态函数作为回调函数。

class ProgramA {
 public:
  void FunA1() { printf("I'am ProgramA.FunA1() and be called..\\n"); }

  static void FunA2() { printf("I'am ProgramA.FunA2() and be called..\\n"); }
};
class ProgramB {
 public:
  void FunB1(void (*callback)()) {
    printf("I'am ProgramB.FunB1() and be called..\\n");
    callback();
  }
};
int main(int argc, char **argv) {
  ProgramA PA;
  PA.FunA1();

  ProgramB PB;
  PB.FunB1(ProgramA::FunA2);
}

在类B中调用类A中的静态函数作为回调函数,从而实现了回调。但这种实现有一个很明显的缺点:static 函数不能访问非static 成员变量或函数,会严重限制回调函数可以实现的功能。

类的非静态函数作为回调函数

这种方式比较麻烦,可以先看一下下面的例子。

class ProgramA {
 public:
  void FunA1() { printf("I'am ProgramA.FunA1() and be called..\\n"); }

  void FunA2() { printf("I'am ProgramA.FunA2() and be called..\\n"); }
};

class ProgramB {
 public:
  void FunB1(void (ProgramA::*callback)(), void *context) {
    printf("I'am ProgramB.FunB1() and be called..\\n");
    ((ProgramA *)context->*callback)();
  }
};
int main(int argc, char **argv) {
  ProgramA PA;
  PA.FunA1();

  ProgramB PB;
  PB.FunB1(&ProgramA::FunA2, &PA);  // 此处都要加&
}

功能总体与上面一个相同,但是,类的静态函数本身不属于该类,所以和普通函数作为回调函数类似。这种方式存在一些不足,,也就我预先还要知道回调函数所属的类定义,当ProgramB想独立封装时就不好用了。(违背了一些设计模式的原则)

Lambda表达式作为回调函数

Lambda本身就是一种匿名函数,是一种函数的简写形式(此处参考上一篇博客Lambda表达式)

#include 
#include
void func1(int a,std::function<void(int)> func2){
  func2(a);
}
int main(int argc, char **argv) {
  auto fun3 = [](int a){
    std::cout<std::endl;
  };
  func1(3,fun3);
}

这种方式也较为简单,但要注意在C++11版本才开始引入Lambda表达式,在一些较为老旧的编译器上可能无法通过。

std::funtion和std::bind的使用

这种方式也是适用于C++,要引入functional的头文件。存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。std::bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。

#include 

#include  // fucntion/bind

class ProgramA {
 public:
  void FunA1() { printf("I'am ProgramA.FunA1() and be called..\\n"); }

  void FunA2() { printf("I'am ProgramA.FunA2() and be called..\\n"); }

  static void FunA3() { printf("I'am ProgramA.FunA3() and be called..\\n"); }
};

class ProgramB {
  typedef std::function<void ()> CallbackFun;
 public:
   void FunB1(CallbackFun callback) {
    printf("I'am ProgramB.FunB2() and be called..\\n");
    callback();
  }
};

void normFun() { printf("I'am normFun() and be called..\\n"); }

int main(int argc, char **argv) {
  ProgramA PA;
  PA.FunA1();

  printf("\\n");
  ProgramB PB;
  PB.FunB1(normFun);
  printf("\\n");
  PB.FunB1(ProgramA::FunA3);
  printf("\\n");
  PB.FunB1(std::bind(&ProgramA::FunA2, &PA));
}

主要看最后一行,通过std::bind函数绑定了对象与对应的函数,这种方式比上面的通过类的成员函数进行回调更为简单方便。下面看一下如果有参数的话,需要引入占位符std::placeholders::_1来进行回调。

#include 
#include 
using namespace std;
 
int TestFunc(int a, char c, float f)
{
    cout << a << endl;
    cout << c << endl;
    cout << f << endl;
 
    return a;
}
 
int main()
{
    auto bindFunc1 = bind(TestFunc, std::placeholders::_1, 'A', 100.1);
    bindFunc1(10);
 
    cout << "=================================\\n";
 
    auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 100.1);
    bindFunc2('B', 10);
 
    cout << "=================================\\n";
 
    auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);
    bindFunc3(100.1, 30, 'C');
 
    return 0;
}

上述例子中引入了占位符std::placeholders::_1,可以有多个,通过下划线加数字来实现,从而实现有参数的回调。这个bind函数中的重载通常第一个是函数的指针,第二个是调用对象的指针,后面跟上参数占位符。
审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4303

    浏览量

    62411
  • 指针
    +关注

    关注

    1

    文章

    479

    浏览量

    70506
  • C++
    C++
    +关注

    关注

    22

    文章

    2104

    浏览量

    73480
  • 回调函数
    +关注

    关注

    0

    文章

    87

    浏览量

    11543
收藏 人收藏

    评论

    相关推荐

    详解C语言函数指针与函数

    在讲函数之前,我们需要了解函数指针。
    发表于 10-19 09:34 772次阅读

    C语言里面的函数指针和函数

    在讲函数之前,我们需要了解函数指针。
    发表于 12-13 10:28 583次阅读

    C 语言函数详解

    C 语言函数详解什么是
    发表于 04-08 10:36

    C/C++函数

    C/C++函数首先看一下回
    发表于 02-11 15:25

    C语言函数学习

    对指针的应用是C语言编程的精髓所在,而回函数就是C语言里面对函数指针的高级应用。简而言之,
    发表于 05-27 09:44 7193次阅读

    C语言函数函数

    来源:嵌入式客栈 1 什么是函数?首先什么是呢? 我的理解是:把段可执行的代码像参数传
    的头像 发表于 09-11 09:57 4097次阅读

    CC++一回事吗

    C89,C++标准是C++99。 我们来介绍C语言和C++中那些不同的地方。 函数默认值 在
    的头像 发表于 11-13 18:18 3253次阅读

    c语言函数的使用及实际作用详解

    大家好,我是无际。今天给大家讲下芯片/模块厂家写SDK必须会使用的种技术:函数
    发表于 11-20 19:51 13次下载
    <b class='flag-5'>c</b>语言<b class='flag-5'>回</b><b class='flag-5'>调</b><b class='flag-5'>函数</b>的使用及实际作用<b class='flag-5'>详解</b>

    详解函数的概念及使用步骤

    函数就是个被作为参数传递的函数。在C语言中,
    的头像 发表于 05-26 15:20 3971次阅读

    详解事件VI

    通过事件注册函数(Register Event Callback)注册VI,在事件发
    的头像 发表于 11-24 09:13 1804次阅读

    介绍在C++中实现的几种方法

    C++中的个重要概念就是类,所以我们般想让类的成员函数作为
    的头像 发表于 01-18 15:09 3525次阅读

    C语言技巧之函数

    在讲函数之前,我们需要了解函数指针。
    的头像 发表于 04-18 11:50 739次阅读

    C语言|函数的不同用法

    函数是个高级操作技巧,也是日常项目中常常使用到的技能。之所以说函数是个高级操作技巧,是因
    发表于 07-10 10:34 1254次阅读

    C++生成Dll与函数测试

    描述了VS环境下,通过C++生成dll的方法,测试函数
    的头像 发表于 08-29 16:05 1505次阅读
    <b class='flag-5'>C++</b>生成Dll与<b class='flag-5'>回</b><b class='flag-5'>调</b><b class='flag-5'>函数</b>测试

    函数(callback)是什么?函数的实现方法

    函数种特殊的函数,它作为参数传递给另函数
    发表于 03-12 11:46 2700次阅读