C/C++回调函数实现与std::function和std::bind介绍

1 概述

  • 回调函数是一种编程模式,指的是将一个函数作为参数传递给另一个函数,并在某个特定事件发生时或满足某些条件时由该函数调用。
  • 这种机制允许你定义在特定事件发生时应执行的代码,从而实现更灵活和模块化的程序设计。

2 传统C/C++回调实现方式

  • 传统C/C++实现回调,主要通过函数指针来实现。

  • 有这样一个场景,某业务系统需要检测环境温度,当温度大于50度时进行告警,告警接口就可以作为回调函数,在温度大于50度时调用。这里通过随机生成一个温度值,模式现实场景。

  • 示例
    *

    c 复制代码
      #include <iostream>
      #include <stdlib.h>
      #include <time.h>
    
      // 定义函数指针
      typedef void(*pCb)(int);
    
      void BusProcess(int tempera, pCb cb) {
          printf("开始业务\n");
    
          printf("业务处理中\n");
    
          // 处理业务过程中,如果温度值大于50摄氏度,则调用告警接口进行告警
          if (tempera > 50) {
              cb(tempera);
          }
    
          printf("结束业务\n");
      }
      
      // 定义回调函数
      void temWarning(int tempera) {
          printf("温度值为: %d 已超阈值,告警 ...\n", tempera);
      }
    
      int main() {
          {
              srand(time(NULL));  
    
              // 随机生成温度值
              int tempera = rand() % 100;
    
              // 开启业务
              BusProcess(tempera, temWarning);
    
          }
          
          system("pause");
          return 0;
      }
  • 打印结果
    *

    复制代码
        开始业务
        业务处理中
        温度值为: 65 已超阈值,告警 ...
        结束业务
        请按任意键继续. . .

3 C++11提供的回调新实现方式

  • 介绍C++11实现回调前先介绍C++11提供的两个新接口std::functionstd::bind

3.1 std::function

  • std::function是一个通用的函数包装器,可以存储任何可调用对象,包括普通函数、类成员函数、仿函数、Lambda表示式。

  • 基本语法
    *

    c 复制代码
      #include <functional>
    
      std::function<返回值类型(参数类型列表)> 函数对象;
  • 示例
    *

    c 复制代码
      #include <iostream>
      #include <functional>
    
      // 普通函数
      int add(int a, int b) {
          return a + b;
      }
    
      class Multiply {
      public:
          int operator()(int a, int b) {
              return a * b;
          }
      };
    
      int main() {
          // 存储普通函数
          std::function<int(int, int)> func1 = add;
          std::cout << "func1 result: " << func1(3, 4) << std::endl;
    
          // 存储 Lambda 表达式
          std::function<int(int, int)> func2 = [](int a, int b) { return a - b; };
          std::cout << "func2 result: " << func2(10, 3) << std::endl;
    
          // 存储函数对象
          Multiply multiply;
          std::function<int(int, int)> func3 = multiply;
          std::cout << "func3 result: " << func3(5, 6) << std::endl;
    
          return 0;
      }

3.2 std::bind

  • std::bind是一个函数模板,用于将函数或成员函数与其参数绑定,生成一个新的可调用对象。

  • 基本语法
    *

    c 复制代码
      // 绑定非类成员函数/变量
      auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
      // 绑定类成员函/变量
      auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
  • 示例
    *

    c 复制代码
      #include <iostream>
      #include <functional>
    
      int add(int a, int b) {
          return a + b;
      }
    
      class MyClass {
      public:
          int multiply(int a, int b) {
              return a * b;
          }
      };
    
      int main() {
          // 绑定普通函数
          auto boundAdd = std::bind(add, std::placeholders::_1, std::placeholders::_2);
    
          std::cout << "Result: " << boundAdd(5, 10) << std::endl; // 输出 15
    
    
          MyClass obj;
    
          // 绑定类成员函数
          auto boundMultiply = std::bind(&MyClass::multiply, &obj, std::placeholders::_1, std::placeholders::_2);
    
          std::cout << "Result: " << boundMultiply(3, 4) << std::endl; // 输出 12
    
          system("pause");
          return 0;
      }

3.3 C++11实现回调

  • 介绍完std::functionstd::bind再看下如何使用C++11语法实现回调。

  • 回调函数为普通函数时示例

    c 复制代码
      #include <iostream>
      #include <stdlib.h>
      #include <time.h>
      #include <functional>
    
      void BusProcess(int tempera, std::function<void(int)> op) {
          printf("开始业务\n");
    
          printf("业务处理中\n");
    
          // 处理业务过程中,如果温度值大于50摄氏度,则调用告警接口进行告警
          if (tempera > 50) {
              op(tempera);
          }
    
          printf("结束业务\n");
      }
    
      void temWarning(int tempera) {
          printf("温度值为: %d 已超阈值,告警 ...\n", tempera);
      }
    
      int main() {
          {
              srand(time(NULL));  
    
              // 随机生成温度值
              int tempera = rand() % 100;
    
              // 开启业务,调用对象为普通函数
              BusProcess(tempera, temWarning);
    
          }
          
          system("pause");
          return 0;
      }
  • 打印结果

    复制代码
        开始业务
        业务处理中
        温度值为: 56 已超阈值,告警 ...
        结束业务
        请按任意键继续. . .
  • 回调函数为类成员对象、函数对象、Lambda时示例

    c 复制代码
      #include <iostream>
      #include <stdlib.h>
      #include <time.h>
      #include <functional>
    
      void BusProcess(int tempera, std::function<void(int)> op) {
          printf("开始业务\n");
    
          printf("业务处理中\n");
    
          // 处理业务过程中,如果温度值大于50摄氏度,则调用告警接口进行告警
          if (tempera > 50) {
              op(tempera);
          }
    
          printf("结束业务\n");
      }
    
      class MyWarn {
      public:
          void startWarning(int tempera) {
              printf("温度值为: %d 已超阈值,告警 ...\n", tempera);
          }
    
          void operator()(int tempera) {
              printf("operator() 温度值为: %d 已超阈值,告警 ...\n", tempera);
          }
      };
    
      int main() {
          {
              srand(time(NULL));
    
              // 随机生成温度值
              int tempera = rand() % 100;
    
              MyWarn mwarn;
    
              std::function<void(int)> fc = std::bind(&MyWarn::startWarning, &mwarn, std::placeholders::_1);
    
              // 调用对象为类成员函数
              BusProcess(tempera, fc);
    
              MyWarn mwarn2;
              std::function<void(int)> fc2 = mwarn2;
    
              // 调用对象为仿函数
              BusProcess(tempera, fc2);
    
    
              // 调用对象为Lambda表达式
              BusProcess(tempera, [](int te) {
                  printf("Lambda 温度值为: %d 已超阈值,告警 ...\n", te);
              });
          }
    
          system("pause");
          return 0;
      }
  • 打印结果

    复制代码
        开始业务
        业务处理中
        温度值为: 66 已超阈值,告警 ...
        结束业务
        开始业务
        业务处理中
        operator() 温度值为: 66 已超阈值,告警 ...
        结束业务
        开始业务
        业务处理中
        Lambda 温度值为: 66 已超阈值,告警 ...
        结束业务
        请按任意键继续. . .
c 复制代码
  class MyWarn {
  public:
      void startWarning(int tempera) {
          printf("温度值为: %d 已超阈值,告警 ...\n", tempera);
      }
  };

  #include <iostream>
  #include <string>
  #include <vector>
  #include <stdlib.h>
  #include <time.h>
  #include <functional>


  class myPersion {
  public:
      myPersion(): mCode(1001), mName("Jack") {
      }

      void setCode(int code) {
          std::cout << "code: " << code << std::endl;
          mCode = code;
      }
  private:
      int mCode;
      std::string mName;
  };


  typedef void(*pCb)(int);

  void optFunc(int data, pCb cb) {
      printf("optFunc ...\n");

      if (data % 2 == 0) {
          cb(data);
      }
  }

  void optFunc2(int data, std::function<void(int)> op) {
      printf("optFunc2 ...\n");

      if (data % 2 == 0) {
          op(data);
      }
  }


  void onHandle(int data) {
      printf("onHandle ...\n");
  }

  int main() {
      {
          srand(time(NULL));  // 初始化随机数生成器
          int radNum = rand() % 100;
          printf("radNum: %d\n", radNum);
          optFunc(radNum, onHandle);

          myPersion mp;

          //optFunc(1001, &mp.setCode);

      }
      
      {
          srand(time(NULL));  // 初始化随机数生成器
          int radNum = rand() % 100;
          printf("radNum: %d\n", radNum);
          optFunc2(radNum, onHandle);
          
          optFunc2(radNum, [](int x) {
              printf("lam ...\n");
          });

          myPersion mp;
          std::function<void(int)> fc = std::bind(&myPersion::setCode, &mp, std::placeholders::_1);
          optFunc2(10010, fc);
      }

      system("pause");
      return 0;
  }
相关推荐
f狐0狸x25 分钟前
【蓝桥杯每日一题】4.1
c语言·c++·算法·蓝桥杯
ん贤25 分钟前
2023第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(真题&题解)(C++/Java题解)
java·c语言·数据结构·c++·算法·蓝桥杯
二进制人工智能4 小时前
【QT5 网络编程示例】TCP 通信
网络·c++·qt·tcp/ip
Android洋芋6 小时前
C语言深度解析:从零到系统级开发的完整指南
c语言·开发语言·stm32·条件语句·循环语句·结构体与联合体·指针基础
莫有杯子的龙潭峡谷6 小时前
3.31 代码随想录第三十一天打卡
c++·算法
Run_Teenage7 小时前
C语言 【初始指针】【指针一】
c语言·开发语言
AaronZZH7 小时前
【进阶】vscode 中使用 cmake 编译调试 C++ 工程
c++·ide·vscode
杨筱毅7 小时前
【性能优化点滴】odygrd/quill 中将 MacroMetadata 变量声明为 constexpr
c++·性能优化
NaZiMeKiY8 小时前
C++ 结构体与函数
开发语言·c++
涛ing8 小时前
【Git “fetch“ 命令详解】
linux·c语言·c++·人工智能·git·vscode·svn