zoukankan      html  css  js  c++  java
  • C++回调函数详解

    1、函数指针

    概念

    一个程序运行时,所有和运行相关的资源都需要被加载到内存中,如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

    使用函数指针实现函数调用

     1 #include <iostream>
     2  3 typedef void (*PINVOKE)(const char *str);
     4  5 void Invoke(const char *str)
     6 {
     7     std::cout << str << std::endl;
     8 }
     9 10 int main()
    11 {
    12     PINVOKE fp = Invoke;
    13     fp("Hello world.");
    14     
    15     return 0;
    16 }

    说明

    函数指针与函数声明的唯一区别就是用指针名(*fp)代替了函数名Invoke,然后进行赋值fp = Invoke就可以进行函数指针的调用了。声明函数指针时要求函数返回值类型、参数个数、参数类型等与已定义函数保持一致。注意,函数指针必须用括号括起来 void (*fp)(char *s)

    2、回调函数

    概念

    声明并定义一个函数A,然后把函数A的指针作为参数传入其他的函数(或系统)中,其他的函数(或系统)在运行时通过函数指针调用函数A,这就是所谓的回调函数。简单来说:回调函数就是一个通过函数指针调用的函数。

    示例

     1 #include <iostream>
     2  3 typedef void (*CALLBACKFUN)(const char *str);
     4  5 void PrintText(CALLBACKFUN fp, const char *str)
     6 {
     7     fp(str);
     8 }
     9 10 void Invoke(const char *str)
    11 {
    12     std::cout << str << std::endl;
    13 }
    14 15 int main()
    16 {
    17     PrintText(Invoke, "Hello world.");
    18     return 0;
    19 }

    类成员函数作为回调函数

    回调函数是基于C - Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函数将发生错误,因为普通的C++成员函数都隐含一个传递函数作为参数,即this指针,C++通过向其他成员函数传递一个指向自身的指针来实现程序函数访问C++数据成员。所以实现类成员函数作为回调函数有两种途径:1、不使用成员函数(使用友元操作符friendC函数访问类的数据成员);2、使用静态成员函数。

    例如:

     1 #include <iostream>
     2  3 class CPrintString
     4 {
     5 public:
     6     void PrintText(const char *str)
     7     {
     8         std::cout << str << std::endl;
     9     }
    10     
    11     static void SPrintText(void *pPs, const char *str)
    12     {
    13         CPrintString *pThis = static_cast<CPrintString *>(pPs);
    14         if(NULL == pPs)
    15         {
    16             return;
    17         }
    18         pThis->PrintText(str);
    19     }
    20 };
    21 22 typedef void (*PRINTTEXT)(void *pPs, const char *str);
    23 24 void CallBackFun(void *pPs, const char *str, PRINTTEXT fp)
    25 {
    26     fp(pPs, str);
    27 }
    28 29 int main()
    30 {
    31     CPrintString obj;
    32     CallBackFun((void *)&obj, "Hello world.", CPrintString::SPrintText);
    33     
    34     return 0;
    35 }

    3、为什么使用回调函数

    一般情况下,回调函数能被普通函数替换,但回调函数最重要的作用是解耦,在这一特点上普通函数代替不了回调函数。

    img

    例子:

     1 #include <stdio.h>
     2 #include <softwareLib.h>     // 包含 Library Function 所在读得 Software library 库的头文件
     3  4 int Callback()              // Callback Function
     5 {
     6     // TODO
     7     return 0;
     8 }
     9 10 int main()                  // Main program
    11 {
    12     // TODO
    13     Library(Callback);
    14     // TODO
    15     return 0;
    16 }

    乍一看,回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,一般情况下库函数对开发人员并不可见,库函数的实现一般不会被修改,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那就只能通过传入不同的回调函数了,这在企业开发中非常常见。

  • 相关阅读:
    nested exception is java.lang.IllegalStateException: No persistence units parsed from {classpath*:META-INF/persistence.xml}
    Thrift Expected protocol id ffffff82 but got 0
    idea
    Activity工作流入门之HelloWorld
    Thrift 入门之helloWorld
    Thrift入门之mac下的安装流程
    netty的解码器与粘包和拆包
    java反射(一)
    使用Spring报错:No default constructor found;
    jpa关联映射(一)
  • 原文地址:https://www.cnblogs.com/horacle/p/15572803.html
Copyright © 2011-2022 走看看