文章目錄
- 引言
- 1.普通函數
- 2.函數指針
- 3.函數對象(仿函數)
- 4.Lambda表達式(匿名函數)
- 5.function
- 6.bind
引言
可調用對象是C++11引入的新概念,可以像函數調用方式的觸發調用的對象就是可調用對象。
c++98可調用對象(普通函數,函數指針,仿函數)
c++11可調用對象(普通函數,函數指針,仿函數,bind生成對象,lambda表達式,function對象)
1.普通函數
bool isEven(int value)
{return (0 == value%2)
}
vector<int> vec{2,5,9,10,11};
auto res = find_if(vec.begin(), vec.end(),isEven);
isEven 就是普通函數可調用對象
2.函數指針
函數指針就是指向普通函數的指針,一般可以理解為函數的引用,不同之處在于一個函數指針可以指向多個普通函數,
只要兩者具有相同的函數簽名格式(即相同的輸入輸出參數,返回值)
/*----------例一-------------------*/
void print_num(int num)
{cout<<"num="<<num;
}
int main()
{void (void*funPtr)(int) = &print_num;funPtr(666);//調用函數指針return 0;
}/*----------例二-------------------*/
bool isEven(int value)
{return (0 == value % 2);
}
//這是一個類型別名定義,EvenDecide被定義為一個函數指針類型,它可以接受一個整數參數并返回布爾值的函數
using EvenDecide = bool(*)(int value);std::vector<int> vec{ 2, 5, 9, 10, 11 };
//這行代碼創建了一個函數指針變量eventDecie,并將函數isEven的地址賦值給它,這樣,evenDecide就可以用來調用isEven函數
EvenDecide evenDecide = isEven;auto res = std::find_if(vec.begin(), vec.end(), evenDecide);
3.函數對象(仿函數)
重載操作符()的類 ,其對象常稱為函數對象
函數對象使用重載的()時,行為類似函數調用,因此也叫仿函數
仿函數是一個類或結構體,它重載了調用運算符operator()。
通過重載這個運算符 我們可以創建一個可調用的對象,就像函數一樣使用它。
仿函數提供了一種將函數調用 和面向對象編程結合起來的方式。
使用仿函數,我們可以將其作為參數傳遞給函數,算法或容器,并在需要的時候 通過運算符來執行相應的操作,它們可以存儲狀態,實現自定義行為并具有更高的靈活性和可定制性。
使用:
函數對象可以像普通函數那樣調用,可以有參數和返回值
函數對象的優點:
函數對象可以有自己的狀態(即對象的成員變量)
函數對象可以作為參數傳遞
/*----------------例一-----------------*/
#include <iostream>
using namespace std;
struct MultiplyBy
{MultiplyBy(int fac) :factor(fac) {}int operator()(int num) {return num * factor;}int factor;
};int main()
{MultiplyBy multiby(10);cout<<"multiby(5)="<<multiby(5);//50return 0;
}/*----------------例二-----------------*/
class EvenJudge
{
public:bool operator() (int value){return (0 == value % 2);}
};std::vector<int> vec{ 2, 5, 9, 10, 11 };EvenJudge evenJudge;
// 1. 通過構造臨時對象,調用operator()重載函數
auto res1 = std::find_if(vec.begin(), vec.end(), EvenJudge());
// 2. 通過對象,隱式調用調用operator()重載函數
auto res2 = std::find_if(vec.begin(), vec.end(), evenJudge);
// 3. 通過對象,顯示調用operator()重載函數
auto res3 = std::find_if(vec.begin(), vec.end(), [&evenJudge](int value) {return evenJudge.operator()(value);
});
4.Lambda表達式(匿名函數)
是一種定義內聯函數的方式,與普通函數不同,匿名函數不需要命名,可以直接在需要的地方定義和使用。
lambda表達式 常用于編寫簡潔,簡單的函數,并且可以輕松的捕獲周圍作用域中的變量。它們非常適用于需要一個簡單,臨時的回調函數或謂詞的情況。
lambda表達式語法
[capture List]->return_type(parameters){body}
//因為lambda表達式會自動推導返回值類型,所以->return_type可以省略
int main()
{int x=5;int y=6;auto sum=[x,&y](){return x+y;}int result = sum();cout<<"Sum="<<result;return 0;
}/*----------------例二-------------------------*/
auto fnIsEven = [](int value) -> bool {return (0 == value % 2);
};auto res4 = std::find_if(vec.begin(), vec.end(), fnIsEven);
auto res5 = std::find_if(vec.begin(), vec.end(), [](int value) {return(0 == value % 2);
});
在上述示例中,Lambda 表達式 x, &y{ return x + y; } 捕獲了變量 x(按值)和 y(按引用),并返回它們的和。通過調用 sum(),可以獲取到捕獲變量的求和結果。
匿名函數提供了一種更簡潔、靈活的方式來編寫內聯函數,特別適合于需要臨時使用的函數功能。
5.function
std::function是一個可調用對象包裝器,是一個類模板,可以容納除了類成員函數指針之外的所有可調用對象,它可以用統一的方式處理函數、函數對象、函數指針,并允許保存和延遲它們的執行。
/*-------------例一-------------*/
#include <functional>
void myFunction(int x) {std::cout << "Value: " << x << std::endl;
}struct MyFunctor {void operator()(int x) const {std::cout << "Value: " << x << std::endl;}
};int main() {std::function<void(int)> func;// 包裝函數指針func = &myFunction;func(42);// 包裝仿函數對象MyFunctor myFunc;func = myFunc;func(42);return 0;
}/*-------------例二-------------*/
# include <iostream>
# include <functional>typedef std::function<int(int, int)> comfun;// 普通函數
int add(int a, int b) { return a + b; }// lambda表達式
auto mod = [](int a, int b){ return a % b; };// 函數對象類
struct divide{int operator()(int denominator, int divisor){return denominator/divisor;}
};int main(){comfun a = add;comfun b = mod;comfun c = divide();std::cout << a(5, 3) << std::endl;std::cout << b(5, 3) << std::endl;std::cout << c(5, 3) << std::endl;
}
std::function可以取代函數指針的作用,因為它可以延遲函數的執行,特別適合作為回調函數使用。它比普通函數指針更加的靈活和便利。
故而,std::function的作用可以歸結于:
1.std::function對C++中各種可調用實體(普通函數、Lambda表達式、函數指針、以及其它函數對象等)的封裝,形成一個新的可調用的std::function對象,簡化調用;
2.std::function對象是對C++中現有的可調用實體的一種類型安全的包裹(如:函數指針這類可調用實體,是類型不安全的)。
6.bind
std::bind可以看作一個通用的函數適配器,它接受一個可調用對象,生成一個新的可調用對象來適應原對象的參數列表。
std::bind將可調用對象與其參數一起進行綁定,綁定后的結果可以使用std::function保存。std::bind主要有以下兩個作用:
將可調用對象和其參數綁定成一個仿函數;
只綁定部分參數,減少可調用對象傳入的參數。
調用bind的一般形式:
auto newCallable = bind(callable, arg_list);
該形式表達的意思是:當調用newCallable時,會調用callable,并傳給它arg_list中的參數。
需要注意的是:arg_list中的參數可能包含形如_n的名字。其中n是一個整數,這些參數是占位符,表示newCallable的參數,它們占據了傳遞給newCallable的參數的位置。數值n表示生成的可調用對象中參數的位置:_1為newCallable的第一個參數,_2為第二個參數,以此類推。
#include <iostream>
#include <functional>class A {
public:void fun_3(int k,int m) {std::cout << "print: k = "<< k << ", m = " << m << std::endl;}
};void fun_1(int x,int y,int z) {std::cout << "print: x = " << x << ", y = " << y << ", z = " << z << std::endl;
}void fun_2(int &a,int &b) {++a;++b;std::cout << "print: a = " << a << ", b = " << b << std::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 3f1(); //print: x=1,y=2,z=3auto f2 = std::bind(fun_1, std::placeholders::_1, std::placeholders::_2, 3);//表示綁定函數 fun 的第三個參數為 3,而fun 的第一,二個參數分別由調用 f2 的第一,二個參數指定f2(1, 2); //print: x=1,y=2,z=3auto f3 = std::bind(fun_1, std::placeholders::_2, std::placeholders::_1, 3);//表示綁定函數 fun 的第三個參數為 3,而fun 的第一,二個參數分別由調用 f3 的第二,一個參數指定//注意: f2 和 f3 的區別。f3(1, 2); //print: x=2,y=1,z=3int m = 2;int n = 3;auto f4 = std::bind(fun_2, std::placeholders::_1, n); //表示綁定fun_2的第一個參數為n, fun_2的第二個參數由調用f4的第一個參數(_1)指定。f4(m); //print: a=3,b=4std::cout << "m = " << m << std::endl; //m=3 說明:bind對于不事先綁定的參數,通過std::placeholders傳遞的參數是通過引用傳遞的,如mstd::cout << "n = " << n << std::endl; //n=3 說明:bind對于預先綁定的函數參數是通過值傳遞的,如nA a;//f5的類型為 function<void(int, int)>auto f5 = std::bind(&A::fun_3, &a, std::placeholders::_1, std::placeholders::_2); //使用auto關鍵字f5(10, 20); //調用a.fun_3(10,20),print: k=10,m=20std::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;
}
編譯并運行:
print: x = 1, y = 2, z = 3
print: x = 1, y = 2, z = 3
print: x = 2, y = 1, z = 3
print: a = 3, b = 4
m = 3
n = 3
print: k = 10, m = 20
print: k = 10, m = 20
由此例子可以看出:
- 預綁定的參數是以值傳遞的形式,不預綁定的參數要用std::placeholders(占位符)的形式占位,從_1開始,依次遞增,是以引用傳遞的形式;
- std::placeholders表示新的可調用對象的第幾個參數,而且與原函數的該占位符所在位置的進行匹配;
- bind綁定類成員函數時,第一個參數表示對象的成員函數的指針,第二個參數表示對象的地址,這是因為對象的成員函數需要有this指針。并且編譯器不會將對象的成員函數隱式轉換成函數指針,需要通過&手動轉換;
- std::bind的返回值是可調用實體,可以直接賦給std::function。
參考一
參考二