C++ bind与回调函数

1 回调函数

  1. 注册回调函数里可以使用functional来统一接口,可以传入函数指针,lambda,bind等!!!
  2. 函数1,2 为一个模块,为回调函数,函数3为一个模块,为注册回调函数,形参为函数指针
  3. 注册回调函数的入参为函数指针,指向回调函数,回调函数实际就是指针类型的实例化

与类无关的回调函数

void basketball()//函数1
{
    printf("选择篮球");
}
void football()//函数2
{
    printf("选择足球");
}
void selectball(void (* ball)())//函数3
{
    printf("选择什么球?");
    ball();
}
int main(void)
{
    selectball(basketball);
    selectball(football);
    return 0;
}

打个比方,一个芯片厂家为了方便用户开发,为芯片写了一个函数库,这个函数库就是一个注册回调函数(如函数3),为什么厂家不直接把函数一套流程全给写出来呢?用户那么多,厂家不可能为每个用户编写出适合其的一整套函数,故就写一个注册回调函数,用户只需要把自己的处理函数(回调函数)名,传给函数指针就行。

#include <iostream>

using namespace std;

typedef void (*Func)(int);
Func p = NULL;

void caller(Func pCallback){
    p = pCallback;
    int result = 1;
    (*p)(result);
}

//void caller(void (*pCallback)(int)){
//    void (*p)(int);
//    p = pCallback;
//    int result = 1;
//    (*p)(result);
//}

void callback(int a){
    cout<< "callback result = " << a << endl;
}

int main(){
    caller(callback);
    getchar();
    return 0;
}

与类有关的回调函数

与类相关的回调函数要注意,类成员函数,如果是非静态,是带有this指针的,会与函数指针的类型不一致(无this),所以需要使用static函数,或者使用bind(&class::handler,this,_1)

#include <iostream>

using namespace std;

typedef void (*FUNP)();//注意参数是没有this的

class Test1{
public:
    Test1(){}
    void func1(FUNP p){
        (*p)();
    }
};

class Test2{
public:
    Test2(){
        Test1 tet1;
        tet1.func1(func2);
    }
    static void func2(){ //作用为fun2为void(*)()类型,一般类函数为void(*)(this)
        cout << "I am Test2" << endl;
    }
};

int main(){
    Test2 tet2;
    return 0;
}

在这里插入图片描述
上述分析,注意,bind和lambda表达式的类型均为functional的,例如上例均为function< void()>,如果用作回调函数,那么回调注册函数的形参类型要写成functional类型,而不是函数指针类型。如果要统一函数指针类型,那么只能使用static静态函数!!!

2 bind函数

C++11 bind和function用法
bind详解

bind原理

  1. auto f = bind(my_handler, 123),表示f() = my_handler(123)
  2. bind函数生成的auto f里,f的参数个数,由bind里的(_1,_2)等的个数决定!!!
  3. bind函数里其他this,123这些表示原函数参数的给定值!!!
    在这里插入图片描述
    ==当需要bind的函数为类成员函数时,由于serrion::handler(this),带有this隐形参数,所以bind绑定一个this指针给f,
    同理,需要注意如果用在回调函数里,可以通过bind,组合成一个新的函数指针,满足原来的注册回调函数使用需求,
    或者使用lambda,[ this] (){this->fun( )}。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
#include <iostream>
#include <functional>
using namespace std;
class A
{
public:
    void fun_3(int k,int m)
    {
        cout<<"print: k="<<k<<",m="<<m<<endl;
    }
};

void fun_1(int x,int y,int z)
{
    cout<<"print: x=" <<x<<",y="<< y << ",z=" <<z<<endl;
}

void fun_2(int &a,int &b)
{
    a++;
    b++;
    cout<<"print: a=" <<a<<",b="<<b<<endl;
}

int main(int argc, char * argv[])
{
    //f1的类型为 function<void(int, int, int)>
    auto f1 = std::bind(fun_1,1,2,3); //表示绑定函数 fun 的第一,二,三个参数值为: 1 2 3
    f1(); //print: x=1,y=2,z=3

    auto f2 = std::bind(fun_1, placeholders::_1,placeholders::_2,3);
    //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别由调用 f2 的第一,二个参数指定
    f2(1,2);//print: x=1,y=2,z=3
 
    auto f3 = std::bind(fun_1,placeholders::_2,placeholders::_1,3);
    //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别由调用 f3 的第二,一个参数指定
    //注意: f2  和  f3 的区别。
    f3(1,2);//print: x=2,y=1,z=3

    int m = 2;
    int n = 3;
    auto f4 = std::bind(fun_2, placeholders::_1, n); //表示绑定fun_2的第一个参数为n, fun_2的第二个参数由调用f4的第一个参数(_1)指定。
    f4(m); //print: m=3,n=4
    cout<<"m="<<m<<endl;//m=3  说明:bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的,如m
    cout<<"n="<<n<<endl;//n=3  说明:bind对于预先绑定的函数参数是通过值传递的,如n
    
    A a;
    //f5的类型为 function<void(int, int)>
    auto f5 = std::bind(&A::fun_3, a,placeholders::_1,placeholders::_2); //使用auto关键字
    f5(10,20);//调用a.fun_3(10,20),print: k=10,m=20

    std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);
    fc(10,20);   //调用a.fun_3(10,20) print: k=10,m=20 
    return 0; 
}

上述例子中的auto 为 std::function<void(int,int)>

3 functional

  1. function是一个template,定义于头文件functional中。通过function<int(int, int)> 声明一个function类型,它是“接受两个int参数、返回一个int类型”的可调用对象,这里可调用对象可以理解为函数指针(指针指向一个函数,该函数有两个int类型参数,返回int类型,即:int (*p)(int, int) )。

  2. 可调用对象:对于一个对象或表达式,如果可以对其使用调用运算符,则称该对象或表达式为可调用对象。

  3. C++语言中有几种可调用对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。

  4. 和其他对象一样,可调用对象也有类型。例如,每个lambda有它自己唯一的(未命名)类类型;函数及函数指针的类型则由其返回值类型和实参类型决定。

function的用法

void printA(int a)
{
     cout << a << endl;          
}

std::function<void(int a)> func;  // 这里定义后,func为函数指针
func = printA;  //这里调用了functional的隐形转换函数
func(2);   //2

lambda

std::function<void()> func_1 = [](){cout << "hello world" << endl;};    
func_1();  //hello world
class Foo{
    Foo(int num) : num_(num){}
    void print_add(int i) const {cout << num_ + i << endl;}
    int num_;  
};

//保存成员函数
std::function<void(const Foo&,int)> f_add_display = &Foo::print_add;
Foo foo(2);
f_add_display(foo,1);

猜你喜欢

转载自blog.csdn.net/weixin_44537992/article/details/105647556