C++函数指针和回调函数详解

嵌入式技术

1330人已加入

描述

一、函数指针

指针是一个变量,是用来指向内存地址。

一个程序运行时,所有和运行相关的东西都需要加载到内存当中,因此可以通过指针指向该内存。

函数是存放在内存代码区域内的,函数名就是函数地址,把这种指向函数入口地址的指针称为函数指针。

Example:

#include < iostream >

typedef int (*fp)(int, int);

int Sum(int lhs, int rhs) {
  return lhs + rhs;
}

int Minus(int lhs, int rhs) {
    return lhs - rhs;
}

int main() {
  int (*randy)(int, int);
  randy = Sum;
  std::cout < < randy(2, 13) < < std::endl;  // 输出:15

  fp sesame = Minus;
  std::cout < < sesame(22, 1) < < std::endl;  // 输出:21
  return 0;
}

可以通过 typedef 的方式简化操作 typedef int (*fp)(int, int); ,也可以直接定义一个函数指针 int (*randy)(int, int);

二、回调函数

当我们把指针作为函数参数时,可以获取该指针所对应的那块内存。

如果这个指针是函数指针,就可以调用该函数的方法进行处理,这个被调用的具体处理的函数就是回调函数。

从上面的例子可以看出来,同一个函数指针,可以指向不同的函数实现,因此可以针对不同的场景,传入不同的函数,而调用方根本不用修改代码。

这就是回调函数灵活的地方,调用者根本不用关注函数如何实现的,只需要关注何时调用即可。

函数指针

回调函数的使用方法

  • 定义一个回调函数;
  • 调用者在使用时,保存回调函数的函数指针,称为“注册”;
  • 调用者发现条件满足时,使用函数指针调用回调函数对事件进行处理。
  • 回调可用于通知机制。比如要写一个多线程下载器,显示下载进度时可以将进度值函数设置为函数,线程内部即可处理,非常方便。

回调函数是继承自C语言的。一般C语言没有多态,所以通过设置回调函数或者钩子函数,达到同样的函数调用不同实现函数的目的。

应用

带参回调函数:

#include < iostream >

// 定义带参回调函数
int callBackFun(int lhs, int rhs) {
 return lhs + rhs;
}

// 定义参数为回调函数的"调用函数"
int callbackRandy(int (*fp)(int, int), int lhs, int rhs) {
 return fp(lhs, rhs);
}

int main() {
 // 运行时执行"调用函数",调用回调函数callBackFun
 int randy = callbackRandy(callBackFun, 2, 13);
 std::cout < < randy < < std::endl; // 输出:15
 return 0;
}

类的静态成员函数和非静态成员函数

#include < iostream >

class Randy {
 public:
  void R1_Non_Static() { 
    std::cout < < "call function R1_Non_Static" < < std::endl;
  }

  static void R2_Static() {
    std::cout < < "call function R2_Static" < < std::endl;
   }
};

class Sesame {
 public:
  void Ses_call1(void (*callBack)()) {
    std::cout < < "call function Ses_call1" < < std::endl;
    callBack();
  }

  void Ses_call2(void (Randy::*callBack)(), void *object) {
    std::cout < < "call function Ses_call2" < < std::endl;
    ((Randy *)object- >*callBack)();
  }
};

int main(int argc, char **argv) {
  Randy randy;

  Sesame sesame;
  sesame.Ses_call2(&Randy::R1_Non_Static, &randy);
  sesame.Ses_call1(Randy::R2_Static);
}

结果:

call function Ses_call2
call function R1_Non_Static
call function Ses_call1
call function R2_Static

上面Ses_call2有个缺陷,就是实现里调用了Randy类,鲁棒性不强,因此可以 增加1层包装

#include < iostream >

class Randy {
 public:
  void R1_Non_Static() { 
    std::cout < < "call function R1_Non_Static" < < std::endl;
  }

  static void R2_Static() {
    std::cout < < "call function R2_Static" < < std::endl;
   }

    // 包装函数
   static void Wrapper(void *objectR) {
    std::cout < < "call function Wrapper" < < std::endl;
    reinterpret_cast< Randy * >(objectR)- >R1_Non_Static();
   }
};

class Sesame {
 public:
  void Ses_call1(void (*callBack)()) {
    std::cout < < "call function Ses_call1" < < std::endl;
    callBack();
  }

  void Ses_call2(void (Randy::*callBack)(), void *object) {
    std::cout < < "call function Ses_call2" < < std::endl;
    ((Randy *)object- >*callBack)();
  }

   // 针对包装函数的调用
  void Ses_call3(void (*callBack)(void *), void *objectS) {
    std::cout < < "call function Ses_call3" < < std::endl;
    callBack(objectS);
  }
};

int main(int argc, char **argv) {
  Randy randy;

  Sesame sesame;
  sesame.Ses_call2(&Randy::R1_Non_Static, &randy);
  sesame.Ses_call1(Randy::R2_Static);
  sesame.Ses_call3(&Randy::Wrapper, &randy);
}

运行结果:

call function Ses_call2
call function R1_Non_Static
call function Ses_call1
call function R2_Static
call function Ses_call3
call function Wrapper
call function R1_Non_Static
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分