1 回调函数
- 注册回调函数里可以使用functional来统一接口,可以传入函数指针,lambda,bind等!!!
- 函数1,2 为一个模块,为回调函数,函数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函数
bind原理
- auto f = bind(my_handler, 123),表示f() = my_handler(123)
- bind函数生成的auto f里,f的参数个数,由bind里的(_1,_2)等的个数决定!!!
- 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
-
function是一个template,定义于头文件functional中。通过function<int(int, int)> 声明一个function类型,它是“接受两个int参数、返回一个int类型”的可调用对象,这里可调用对象可以理解为函数指针(指针指向一个函数,该函数有两个int类型参数,返回int类型,即:int (*p)(int, int) )。
-
可调用对象:对于一个对象或表达式,如果可以对其使用调用运算符,则称该对象或表达式为可调用对象。
-
C++语言中有几种可调用对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。
-
和其他对象一样,可调用对象也有类型。例如,每个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);