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);