c++回调函数详解及实现(lambda)

简介: c++回调函数详解及实现(lambda)

回调函数就是一个被作为参数传递的函数。在C语言中,回调函数只能使用函数指针实现,在C++语言中还可以使用仿函数或匿名函数。回调函数的使用可以大大提升编程的效率,这使得它在现代编程中被非常多地使用。


回调的好处


用于解耦,可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。


回调还可用于通知机制。当某一事件发生时,如果使用者注册过了回调函数,则会自动执行回调函数中的内容。


回调还可用于预留逻辑,比如当时不确定或者根本不确定某一条件下要执行什么操作,就把选择权留给使用者,让使用者来实现相应的逻辑。比如某些函数库,排序算法的实现,为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑。


比如应用开发者和内核之间,应用者注册了信号处理,实则就是一种回调注册。


如下图所示:



回调的使用


⑴定义一个回调函数。


⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者。


⑶当特定的事件或条件发生的时候,调用者使永函数指针调用回调函数对事件进行处理。


c++回调的实现


网上的例子大多太旧,没有用到现代c++的特性,还是以往函数指针的实现。


以下示例为使用现在c++14以上实现,包括lambda表达式的使用。


#include <functional>
#include <iostream>
class MyTest{
public:
    MyTest() = default;
    void doCalc(){
        //干其他事,完了
        // 执行回调
        if(myCallBack!= nullptr){
            myCallBack(1,2);
        }
    }
    using callback_t = std::function<void(const int &a, const int &b)>;
    // 注册回调
    void setCallBackHandler(const callback_t &cb){
        myCallBack = cb;
    }
private:
    // 定义回调
    callback_t myCallBack;
};
// 回调函数
void handleCallBack(const int &a,const int &b){
    std::cout << "this is from callback handleCallBack"<<std::endl;
}
int main(){
    MyTest t;
    // 回调函数
    auto f= [](const int &a,const int &b){
        std::cout << "this is from callback f"<<std::endl;
    };
    // 注册回调
    // 写法一
    t.setCallBackHandler(f);
    // 写法二
    t.setCallBackHandler([&f](auto &&a, auto &&b) {
        f(std::forward<decltype(a)>(a), std::forward<decltype(b)>(b));
    });
    // 写法三
    t.setCallBackHandler([](auto &&a, auto &&b) {
        handleCallBack(std::forward<decltype(a)>(a), std::forward<decltype(b)>(b));
    });
    t.doCalc();
}


以上对形参使用了类似如auto的写法,这是c++14的特性。


至于参数为啥要用&&?是为了做到完美转发。要做到这一点,参数 必须成为通用引用(见条款 24)。


条款 33:对 auto&&形参使用 decltype 来 std::forward。

C++14 最令人兴奋的特性之一是在参数规范中使用 auto 的泛型 lambda。


在 lambda 中,我们可以通过检查的参数 x 的类型,来判断实参是左值还是右值。


条款 28 解释到,如果将左值实参传递给通用引用,该参数的类型将成为左值引用,如果传递的是右值,该参数将成为一个右值引用。这意味着,在 lambda 中,我们可以通过检查的参数 x 的类型,来判断实参是左值还是右值。


decltype(见条款 3)给了我们一个实现途径。如果是左值,decltype(x)得到的类型是左值引用,如果是右值,decltype(x)得到的是右值引用。


需要铭记的是:对 auto&&形参使用 decltype 来 std::forward。


以上条款详细内容参见《Effective.Modern.C++》一书。


c语言的实现


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int (callBack)(const void *buffer,size_t size,char *p_out);
void callFunc(callBack *my_callback, char *p_out) {
    printf("callFunc\n");
    const void *buffer = NULL;
    my_callback(buffer,0,p_out); //传入值可以随便填
}
int callBackFunc(const void *buffer, size_t size, char *p_out){
    printf("callBackFunc\n");
    memset(p_out,0x00,sizeof(char)*100);
    strcpy(p_out,"this is Callback:this is string.");
    return 1;
}
int main(int argc,char *args[]){
    char p_out[100];
    callFunc(callBackFunc,p_out);
    printf("%s\n",p_out);
    return 0;
}


引用


什么是回调函数?为什么要使用回调函数?如何使用回调函数?_llzhang_fly的博客-CSDN博客_回调函数


C++回调函数的基本理解和使用_一度凡尘的博客-CSDN博客_回调函数


回调函数的实质——什么是回调函数,为什么要使用回调函数_斗趣的博客-CSDN博客_回调函数


c++11线程池的实现原理及回调函数的使用_特立独行的猫a的博客-CSDN博客_c++多线程回调函数深入理解:回调函数_极客点儿的博客-CSDN博客_回调函数


C++学习之回调函数_欧特克_Glodon的博客-CSDN博客


关于C++ 回调函数(callback) 精简且实用_zhoupian的博客-CSDN博客_c++ callback

相关文章
|
7月前
|
算法 编译器 C++
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits 判断 Lambda表达式类型?
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits 判断 Lambda表达式类型?
137 4
|
7月前
|
存储 算法 程序员
【C++20 新特性 】模板参数包展开与Lambda初始化捕获详解
【C++20 新特性 】模板参数包展开与Lambda初始化捕获详解
371 3
|
25天前
|
算法 编译器 C++
【C++11】lambda表达式
C++11 引入了 Lambda 表达式,这是一种定义匿名函数的方式,极大提升了代码的简洁性和可维护性。本文详细介绍了 Lambda 表达式的语法、捕获机制及应用场景,包括在标准算法、排序和事件回调中的使用,以及高级特性如捕获 `this` 指针和可变 Lambda 表达式。通过这些内容,读者可以全面掌握 Lambda 表达式,提升 C++ 编程技能。
59 3
|
7月前
|
存储 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(下)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
110 5
|
3月前
|
算法 编译器 程序员
C++ 11新特性之Lambda表达式
C++ 11新特性之Lambda表达式
18 0
|
5月前
|
存储 C++ 运维
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
55 6
|
5月前
|
安全 编译器 C++
C++一分钟之-泛型Lambda表达式
【7月更文挑战第16天】C++14引入泛型lambda,允许lambda接受任意类型参数,如`[](auto a, auto b) { return a + b; }`。但这也带来类型推导失败、隐式转换和模板参数推导等问题。要避免这些问题,可以明确类型约束、限制隐式转换或显式指定模板参数。示例中,`safeAdd` lambda使用`static_assert`确保只对算术类型执行,展示了一种安全使用泛型lambda的方法。
70 1
|
6月前
|
算法 编译器 C++
C++一分钟之—Lambda表达式初探
【6月更文挑战第22天】C++的Lambda表达式是匿名函数的快捷方式,增强函数式编程能力。基本语法:`[capture](params) -&gt; ret_type { body }`。例如,简单的加法lambda:`[](int a, int b) { return a + b; }`。Lambda可用于捕获外部变量(值/引用),作为函数参数,如在`std::sort`中定制比较。注意点包括正确使用捕获列表、`mutable`关键字和返回类型推导。通过实践和理解这些概念,可以写出更简洁高效的C++代码。
57 13
|
6月前
|
C++
C++语言的lambda表达式
C++从函数对象到lambda表达式以及操作参数化
|
7月前
|
算法 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(中)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
66 2