• c++ 回调函数,std::function,std::bind


    回调函数

    回调函数的创建步骤大概为:

    1. 声明一个函数指针类型。
    2. 拟写使用回调函数的函数,将函数指针类型及变量名声明作为参数传递。
    3. 拟写符合函数指针类型的实现函数,将实现函数的指针作为参数传递给使用它的函数。

    定义回调函数的指针类型,包括返回值类型、(*类型名)函数指针、参数表

    typedef int (*Calc)(int a, int b);
    
    • 1

    回调函数的使用者函数

    int CalcValue(int a, int b, const Calc &func) {
        return func(a, b);
    }
    
    • 1
    • 2
    • 3

    符合函数指针类型的实现函数

    int Add(int a, int b) {
        return a + b;
    }
    
    • 1
    • 2
    • 3

    实现函数的类型必须要和函数指针的类型声明一致,也就是返回值和参数表(个数、类型)要完全一致。

    typedef int (*Calc)(int a, int b);
    int CalcValue(int a, int b, const Calc &func) {
        return func(a, b);
    }
    int Add(int a, int b) {
        return a + b;
    }
    int main()
    {
        int a = 4;
        int b = 6;
        int c = CalcValue(a, b, Add);
        std::cout << "c: " << c << std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    std::function

    可调用对象:

    • 一个函数指针
    • 一个具有operator()成员函数的类对象(传说中的仿函数),lambda表达式
    • 一个可被转换为函数指针的类对象
    • 一个类成员(函数)指针
    • bind表达式或其它函数对象

    std::function 是一个模板类。作用是对C++中的可调用对象进行包装,例如普通函数、成员函数、模板函数、静态函数、lambda表达式等。可以把std::function看做一个函数对象,用于表示函数这个抽象概念。std::function的实例可以存储、复制和调用任何可调用对象,存储的可调用对象称为std::function的目标,若std::function不含目标,则称它为空,调用空的std::function的目标会抛出std::bad_function_call异常。

    最基本的作用是,简化调用的复杂程度,统一调用的方式。如果代码中混杂着大量普通函数、模板函数、lambda,使用 std::function 是比较适合的。

    std::function<returnType(argType, argType,...)> func;
    
    • 1

    模板类当中对类型的声明方式是 < 返回值类型 ( 参数类型1, 参数类型2, …) >

    使用场景:

    1. 绑定一个函数(普通函数或者静态函数)
    2. 实现回调函数
    3. 作为函数入参
    std::function<void(int)> f; // 这里表示function的对象f的参数是int,返回值是void
    #include 
    #include 
    
    struct Foo {
        Foo(int num) : num_(num) {}
        void print_add(int i) const { std::cout << num_ + i << '\n'; }
        int num_;
    };
    
    void print_num(int i) { std::cout << i << '\n'; }
    
    struct PrintNum {
        void operator()(int i) const { std::cout << i << '\n'; }
    };
    
    int main() {
        // 存储自由函数
        std::function<void(int)> f_display = print_num;
        f_display(-9);
    
        // 存储 lambda
        std::function<void()> f_display_42 = []() { print_num(42); };
        f_display_42();
    
        // 存储到 std::bind 调用的结果
        std::function<void()> f_display_31337 = std::bind(print_num, 31337);
        f_display_31337();
    
        // 存储到成员函数的调用
        std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
        const Foo foo(314159);
        f_add_display(foo, 1);
        f_add_display(314159, 1);
    
        // 存储到数据成员访问器的调用
        std::function<int(Foo const&)> f_num = &Foo::num_;
        std::cout << "num_: " << f_num(foo) << '\n';
    
        // 存储到成员函数及对象的调用
        using std::placeholders::_1;
        std::function<void(int)> f_add_display2 = std::bind(&Foo::print_add, foo, _1);
        f_add_display2(2);
    
        // 存储到成员函数和对象指针的调用
        std::function<void(int)> f_add_display3 = std::bind(&Foo::print_add, &foo, _1);
        f_add_display3(3);
    
        // 存储到函数对象的调用
        std::function<void(int)> f_display_obj = PrintNum();
        f_display_obj(18);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    #include 
    class A
    {
        std::function<void()> callback_;
    public:
        A(const std::function<void()>& f) :callback_(f) {};
        void notify(void)
        {
            callback_();
        }
    };
    class Foo {
    public:
        void operator()(void)
        {
            std::cout << __FUNCTION__ << std::endl;
        }
    };
    int main(void)
    {
        Foo foo;
        A aa(foo);
        aa.notify();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    #include 
    void call_when_even(int x, const std::function<void(int)>& f)
    {
        if (!(x & 1))
        {
            f(x);
        }
    }
    void output(int x)
    {
        std::cout << x << " ";
    }
    int main(void)
    {
        for (int i = 0; i < 10; ++i)
        {
            call_when_even(i, output);
        }
        std::cout << std::endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    std::bind

    是一个基于模板的函数,作用是绑定并返回一个 std::function 对象。
    那么什么是“绑定”?它本身作为延迟计算的思想的一种实现,作为一个调用过程当中的转发者而存在,返回一个 std::function 对象。
    它与 std::function 不同的是,function 是模板类,bind 是模板函数,而 bind 返回的可调用对象可以直接给 function 进行包装并保存。

    为什么要进行“包装”与“转发”呢?
    首先,不规范的解释是,function 的作用是包装,它可以包装类成员函数,但却无法生成类成员函数的可调用对象。而 std::bind 则是可以生成。
    因此,function 与 bind 结合后,便成为了 C++ 中类成员函数作为回调函数的一种规范的实现方式。

    std::bind(&funcName, std::placeholders::_1, ...);
    
    • 1

    当用作普通函数的绑定时,第一个参数是可调用对象(普通函数、lambda等),而第二个参数开始对应可调用对象的参数表。
    std::placeholders::_1 代表可调用对象的第一个参数,_2就代表第二个参数,依此类推。

    当用作类成员函数的绑定时,第一个参数仍然是作为类成员的可调用对象引用,第二个参数则是对象的指针,而第三个参数开始对应可调用对象的参数表。同样使用 std::placeholders::_* 依次向后推。

    因为类成员函数都有一个默认的参数,this,作为第一个参数,这就导致了类成员函数不能直接赋值给std::function,需要std::bind,简言之,std::bind的作用就是转换函数签名,将缺少的参数补上,将多了的参数去掉,甚至还可以交换原来函数参数的位置。

    注意

    1. 调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是引用或指针(可以包含智能指针,如 std::shared_ptr 与std::unique_ptr),指向将访问其成员的对象。
    2. 到 bind 的参数被复制或移动,而且决不按引用传递,除非包装于 std::ref 或 std::cref 。
    3. 允许同一 bind 表达式中的多重占位符(例如多个 _1 ),但结果仅若对应参数( u1 )是左值或不可移动右值才良好定义。
    class Baseclass
    {
    public:
        int Add(int a, int b) { return a + b; };
    };
    int main()
    {
        int a = 1;
        int b = 2;
        std::shared_ptr<Baseclass> ptr_class = std::make_shared<Baseclass>();
        std::function<int(int, int)> addFunc = std::bind(&Baseclass::Add, ptr_class, std::placeholders::_1, std::placeholders::_2);
    
        int c = addFunc(a, b);
        std::cout << "c: " << c << std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    参考
    c++ 回调函数与std::function使用实例

  • 相关阅读:
    计算机毕业设计Python+Django的监控管理系统(源码+系统+mysql数据库+Lw文档)
    创建多层级行索引,创建多层级行索引的DataFrameMultiIndex.from_product()
    怎么分辨CN2 GIA,CN2 GT,CTG,163直连,国际线路?
    C++Primer Plus第十一章类的使用,课后练习1,还是醉汉回家的故事
    使用OpenCVSharp利用PictrueBox对接摄像头获取视频图像
    独立站怎么搭建?搭建一个独立站的10个建议和步骤
    河工计院ACM2022寒假培训题单以及超详细题解
    < 每日技巧: JavaScript代码优化 >
    Oracle-expdp报错ORA-08103: object no longer exists
    C++模拟OpenGL库——图片处理及纹理系统(三):图片缩放操作:简单插值&二次线性插值
  • 原文地址:https://blog.csdn.net/weixin_44347020/article/details/132690175