C++11中bind綁定器和function函數對象介紹
一. bind1st和bind2nd
1.C++ STL中的綁定器
- bind1st:operator()的第一個形參變量綁定成一個確定的值
- bind2nd:operator()的第二個形參變量綁定成一個確定的值
C++11從Boost庫中引入瞭bind綁定器和function函數對象機制
bind可用於給多元函數降元:Bind + 二元函數對象 = 一元函數對象
#include<iostream> #include<vector> #include<functional> #include<algorithm>//泛型算法 #include<ctime> using namespace std; template<typename Container> void showContainer(Container& con) { //typename Container::iterator it=con.begin(); auto it = con.begin(); for (; it != con.end(); ++it) { cout << *it << " "; } cout << endl; } int main() { vector<int> vec; srand(time(nullptr)); for (int i = 0; i < 20; ++i) { vec.push_back(rand() % 100 + 1); } showContainer(vec); sort(vec.begin(), vec.end());//默認從小到大排序 showContainer(vec); //greater需要二元函數對象 sort(vec.begin(), vec.end(), greater<int>());//從大到小排序 showContainer(vec); /* 把70按順序插入到vec容器中 ->找第一個小於70的數字 operator()(const T &val) greater a>b less a<b 綁定器+二元函數對象=》一元函數對象 bind1st:+greater bool operator()(70,const_Ty&_Right) bind2nd:+less bool operator()(const_Ty &_Left,70) */ auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 70)); if (it1 != vec.end()) { vec.insert(it1, 70); } showContainer(vec); return 0; }
2.bind1st和bind2nd的底層原理實現
綁定器本身是一個函數對象
#include<iostream> #include<vector> #include<functional> #include<algorithm> #include<ctime> using namespace std; template<typename Container> void showContainer(Container& con) { auto it = con.begin(); for (; it != con.end(); ++it) { cout << *it << " "; } cout << endl; } //遍歷兩個迭代器之間的元素,如果滿足函數對象的運算,就返回當前的迭代器,如果都不滿足就返回end template<typename Iterator,typename Compare> Iterator my_find_if(Iterator first, Iterator last, Compare comp) { //這裡傳入的comp是封裝好的一元函數對象 for (; first != last; ++first) { if (comp(*first))//獲取容器的一個元素 { return first; } } return last; } template<typename Compare,typename T> class _mybind1st//綁定器是函數對象的一個應用 { public: //這裡傳入的comp是二元函數對象 _mybind1st(Compare comp,T val) :_comp(comp),_val(val){} //通過重載operator()把二元函數對象封裝為一元函數對象 bool operator()(const T& second) { return _comp(_val, second); } private: Compare _comp; T _val; }; template<typename Compare,typename T> _mybind1st<Compare, T> mybind1st(Compare comp, const T& val) { //直接使用函數模板,好處是可以進行類型的推演 //這裡傳入的comp是一個二元函數對象 //通過二元函數對象構造一元函數對象 //綁定器本身是一個函數對象,也就是重載瞭operator() return _mybind1st<Compare, T>(comp, val); } int main() { vector<int> vec; srand(time(nullptr)); for (int i = 0; i < 20; ++i) { vec.push_back(rand() % 100 + 1); } showContainer(vec); sort(vec.begin(), vec.end());//默認從小到大排序 showContainer(vec); //greater需要二元函數對象 sort(vec.begin(), vec.end(), greater<int>());//從大到小排序 showContainer(vec); auto it1 = my_find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 70)); if (it1 != vec.end()) { vec.insert(it1, 70); } showContainer(vec); return 0; }
二. 模板的完全特例化和非完全特例化
有完全特例化優先匹配完全特例化,有部分特例化就匹配部分特例化,沒有的話就從原模板自己實例化
#include<iostream> using namespace std; template<typename T> class Vector { public: Vector() { cout << "call Vector template init" << endl; } }; //對char*類型提供完全特例化版本 template<> class Vector<char*> { public: Vector() { cout << "call Vector<char*> init" << endl; } }; //對指針類型提供的部分特例化版本(部分:隻知道是個指針,但是指針的類型是什麼不知道) template<typename Ty> class Vector<Ty*> { public: Vector() { cout << "call Vector<Ty*> init" << endl; } }; //指針函數指針(有返回值,有兩個形參變量)提供的部分特例化 template<typename R,typename A1,typename A2> class Vector<R(*)(A1, A2)> { public: Vector() { cout << "call Vector<R(*)(A1,A2)> init" << endl; } }; //針對函數(有一個返回值,有兩個形參變量)類型提供的部分特例化 template<typename R, typename A1, typename A2> class Vector<R(A1, A2)> { public: Vector() { cout << "call Vector<R(A1,A2)> init" << endl; } }; int sum(int a, int b) { return a + b; } int main() { Vector<int> vec1; Vector<char*> vec2; Vector<int*> vec3; Vector<int(*)(int, int)> vec4; Vector<int(int, int)> vec5; //註意區分函數類型和函數指針類型 typedef int(*PFUNC1)(int, int); PFUNC1 pfunc1 = sum; cout << pfunc1(10, 20) << endl; typedef int PFUNC2(int, int); PFUNC2* pfunc2 = sum; cout << (*pfunc2)(10, 20) << endl; return 0; }
#include<iostream> #include<typeinfo> using namespace std; //T包含瞭所有大的類型 template<typename T> void func(T a) { cout << typeid(T).name() << endl; } int sum(int a, int b) { return a + b; } //把所有形參類型都取出來 template<typename R, typename A1, typename A2> void func2(R(*a)(A1, A2)) { cout << typeid(R).name() << endl; cout << typeid(A1).name() << endl; cout << typeid(A2).name() << endl; } template<typename R,typename T,typename A1,typename A2> void func3(R(T::*a)(A1, A2)) { cout << typeid(R).name() << endl; cout << typeid(T).name() << endl; cout << typeid(A1).name() << endl; cout << typeid(A2).name() << endl; } class Test { public: int sum(int a, int b) { return a + b; } }; int main() { //func(10);//int //func("aaa");//const char * func(sum); func2(sum); func3(&Test::sum); return 0; }
三. function函數對象
綁定器,函數對象,lambda表達式本質上都是函數對象,隻能使用在一條語句中,但是如果想要在多條語句中使用,就需要function
使用function函數需要註意:
- 用函數類型實例化function;
- 通過function調用
operator()
函數的時候,需要根據函數類型傳入相應的參數。
#include<iostream> #include<functional> using namespace std; void hello1() { cout << "hello world!" << endl; } void hello2(string str) { cout << str << endl; } int sum(int a, int b) { return a + b; } int main() { //從function的模板定義處,看到希望用一個函數類型實例化function function<void()> func1 = hello1; func1();//func1.operator() => hello1() function<void(string)> func2 = hello2; func2("hello hello2!"); function<int(int, int)> func3 = sum; cout << func3(2, 3) << endl; function<int(int, int)> func4 = [](int a, int b)->int {return a + b; }; cout << func4(3, 4) << endl; return 0; }
function不僅可以留下普通全局函數的類型,對於類的成員方法也可以進行類型保留:
#include<iostream> #include<functional> using namespace std; class Test { public://必須依賴一個對象void(Test::*pfunc)(string) void hello(string str) { cout << str << endl; } }; int main() { //成員方法一經編譯都會多一個當前類型的this指針 function<void (Test*, string)> func = &Test::hello; Test t; //對於成員方法的調用需要依賴一個成員對象 func(&t, "call Test::hello!"); return 0; }
function的特點:可以把所有函數、綁定器、函數對象和lambda表達式的類型保留起來,在其他地方都可以使用。否則綁定器、lambda表達式就隻能使用在語句中。
#include<iostream> #include<functional> #include<map> using namespace std; void doShowAllBooks(){ cout << "查看所有書籍信息" << endl; } void doBorrow() { cout << "借書" << endl; } void doBack() { cout << "還書" << endl; } void doQueryBooks() { cout << "查詢書籍" << endl; } void doLoginOut() { cout << "註銷" << endl; } int main() { int choice = 0; //使用function函數對象完成 map<int, function<void()>> actionMap; actionMap.insert({ 1,doShowAllBooks }); actionMap.insert({ 2,doBorrow }); actionMap.insert({ 3,doBack }); actionMap.insert({ 4,doQueryBooks }); actionMap.insert({ 5,doLoginOut }); for (;;) { cout << "------------------" << endl; cout << "1.查看所有書籍信息" << endl; cout << "2.借書" << endl; cout << "3.還書" << endl; cout << "4.查詢書籍" << endl; cout << "5.註銷" << endl; cout << "------------------" << endl; cout << "請選擇:"; cin >> choice; auto it = actionMap.find(choice); if (it == actionMap.end()) { cout << "輸入數字無效,重新選擇" << endl; } else { it->second(); } //不好,因為這塊代碼無法閉合,無法做到“開-閉”原則,也就是說這塊代碼隨著需求的更改需要一直改,永遠也閉合不瞭,避免不瞭要產生很多問題 /* switch(choice) { case 1:break; case 2:break; case 3:break; case 4:break; case 5:break; default:break; } */ } return 0; }
function的實現原理:
#include<iostream> #include<functional> using namespace std; void hello(string str) { cout << str << endl; } int sum(int a, int b) { return a + b; } template<typename Fty> class myfunction{}; /* template<typename R,typename A1> class myfunction<R(A1)> { public: //typedef R(*PFUNC)(A1); using PFUNC = R(*)(A1); myfunction(PFUNC pfunc):_pfunc(pfunc){} R operator()(A1 arg) { return _pfunc(arg); } private: PFUNC _pfunc; }; template<typename R, typename A1,typename A2> class myfunction<R(A1,A2)> { public: //typedef R(*PFUNC)(A1); using PFUNC = R(*)(A1,A2); myfunction(PFUNC pfunc) :_pfunc(pfunc) {} R operator()(A1 arg1,A2 arg2) { return _pfunc(arg1,arg2); } private: PFUNC _pfunc; }; */ //...表示可變參,A表示的是一組1類型,個數任意 template<typename R, typename... A> class myfunction<R(A...)> { public: using PFUNC = R(*)(A...); myfunction(PFUNC pfunc) :_pfunc(pfunc) {} R operator()(A... arg) { return _pfunc(arg...); } private: PFUNC _pfunc; }; int main() { myfunction<void(string)> func1(hello); func1("hello world"); myfunction<int(int, int)> func2(sum); cout << func2(10, 20) << endl; return 0; }
四. bind和function實現線程池
#include<iostream> #include<functional> using namespace std; using namespace placeholders; //C++11 bind 綁定器=>返回的結果是一個函數對象 void hello(string str) { cout << str << endl; } int sum(int a, int b) { return a + b; } class Test { public: int sum(int a, int b) { return a + b; } }; int main() { //bind是函數模板,可以自動推演模板類型參數 bind(hello, "Hello bind!")(); cout << bind(sum, 20, 30)() << endl; cout << bind(&Test::sum, Test(), 20, 30)() << endl; //function隻接受一個類型,綁定器可以給相應的函數綁定固定的參數,綁定器隻能使用在語句當中 //參數占位符,綁定器出瞭語句,無法繼續使用 bind(hello, _1)("hello bind 2"); cout << bind(sum, _1, _2)(20, 30) << endl; //此處把bind返回的綁定器binder就復用起來瞭 function<void(string)> func1 = bind(hello, _1); func1("hello china!"); func1("hello shan xi!"); func1("hello da li!"); }
#include<iostream> #include<functional> #include<thread> #include<vector> using namespace std; using namespace placeholders; //線程類 class Thread { public: Thread(function<void(int)> func,int no):_func(func),_no(no){} thread start() { thread t(_func,_no); return t; } private: function<void(int)> _func; int _no; }; //線程池類 class ThreadPool { public: ThreadPool(){} ~ThreadPool() { //釋放thread對象占用的堆資源 for (int i = 0; i < _pool.size(); i++) { delete _pool[i]; } } //開啟線程池 void startPool(int size) { for (int i = 0; i < size; i++) { //不管是C++裡面的thread還是Linux裡面的pthread需要的線程函數都是一個C函數,是不能夠使用成員方法的,因為它是C的函數類型,不可能把成員方法的函數指針給一個C的函數指針,接收不瞭。所以就需要綁定,把runInThread所依賴的參數全部綁定 _pool.push_back(new Thread(bind(&ThreadPool::runInThread, this, _1),i)); } for (int i = 0; i < size; i++) { _handler.push_back(_pool[i]->start()); } for (thread& t : _handler) { t.join(); } } private: vector<Thread*> _pool; vector<thread> _handler; //把runInThread這個成員方法充當線程函數 void runInThread(int id) { cout << "call runInThread! id:" << id << endl; } }; int main() { ThreadPool pool; pool.startPool(10); return 0; }
五. lambda表達式
- 函數對象的應用:使用在泛型算法參數傳遞、比較性質、自定義操作、優先級隊列和智能指針
- 函數對象的缺點:需要先定義一個函數對象類型,但是類型定義完後可能隻是用在瞭定義的地方,後面可能不會再用瞭,沒有必要為瞭需要一個函數對象定義一個類型,這個類型就永遠在代碼當中。
C++11函數對象的升級版 => lambda表達式:
- lambda表達式:底層依賴函數對象的機制實現的。
- lambda表達式語法:[捕獲外部變量](形參列表) ->返回值{操作代碼};
如果lambda表達式的返回值不需要,那麼“->返回值”可以省略
[捕獲外部變量]
- [ ]:表示不捕獲任何外部變量
- [=]:以傳值的方式捕獲外部的所有變量
- [&]:以傳引用的方式捕獲外部的所有變量[this]:捕獲外部的this指針
- [=,&a]:以傳值的方式捕獲外部的所有變量,但是a變量以傳引用的方式捕獲
- [a,b]:以傳值的方式捕獲外部變量a和b
- [a,&b]:a以值傳遞捕獲,b以傳引用的方式捕獲
1.lambda表達式的實現原理
#include<iostream> using namespace std; template<typename T=void> class TestLambda01 { public: void operator()() { cout << "hello world" << endl; } }; template<typename T = int> class TestLambda02 { public: TestLambda02() {} int operator()(int a, int b) { return a + b; } }; template<typename T = int> class TestLambda03 { public: TestLambda03(int a,int b):ma(a),mb(b){} void operator()()const { int tmp = ma; ma = mb; mb = tmp; } private: mutable int ma; mutable int mb; }; class TestLambda04 { public: TestLambda04(int &a,int &b):ma(a),mb(b){} void operator()()const { int tmp = ma; ma = mb; mb = tmp; } private: int& ma; int& mb; }; int main() { auto func1 = []()->void {cout << "hello world" << endl; }; func1(); auto func2 = [](int a, int b)->int {return a + b; }; cout << func2(20, 30) << endl; int a = 10; int b = 20; //按值傳遞a,b值未被改變 auto func3 = [a, b]()mutable { int tmp = a; a = b; b = tmp; }; func3(); cout << "a:" << a << " b:" << b << endl; //傳引用值a,b值被改變 auto func4 = [&]() { int tmp = a; a = b; b = tmp; }; func4(); cout << "a:" << a << " b:" << b << endl; cout << "--------------------" << endl; TestLambda01<> t1; t1(); TestLambda02<> t2; cout << t2(20, 30) << endl; TestLambda03<> t3(a,b); t3(); cout << "a:" << a << " b:" << b << endl; TestLambda04 t4(a,b); t4(); cout << "a:" << a << " b:" << b << endl; return 0; }
mutable
:成員變量本身也不是常量,隻不過在常方法中this指針被修飾成const,在聲明成員變量前加mutable
,可以在const
方法中修改普通的成員變量
lambda表達式後面修飾mutable相當於在它的所有成員變量添加一個mutable修飾。
2.lambda表達式的應用實踐
lambda表達式應用於泛型算法:
#include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { vector<int> vec; for (int i = 0; i < 20; ++i) { vec.push_back(rand() % 100 + 1); } sort(vec.begin(), vec.end(), [](int a, int b)->bool { return a > b; }); for (int val : vec) { cout << val << " "; } cout << endl; //65按序插入序列 要找一個小於65的數字 auto it = find_if(vec.begin(), vec.end(), [](int a)->bool {return a < 65; }); if (it != vec.end()) { vec.insert(it, 65); } for (int val : vec) { cout << val << " "; } cout << endl; for_each(vec.begin(), vec.end(), [](int a) { if (a % 2 == 0) cout << a << " "; }); cout << endl; return 0; }
既然lambda表達式隻能使用在語句中,如果想跨語句使用之前定義好的lambda表達式,采用function類型來表示函數對象的類型。
哈希表的應用:
#include<iostream> #include<vector> #include<map> #include<functional> using namespace std; int main() { //auto隻能出現在根據右邊表達式推導左邊的類型,隻能使用在函數的局部作用域的范圍之內 //預先lambda表達式不知道需要先存儲lambda表達式類型 map<int, function<int(int, int)>> caculateMap; caculateMap[1] = [](int a, int b)->int {return a + b; }; caculateMap[2] = [](int a, int b)->int {return a - b; }; caculateMap[3] = [](int a, int b)->int {return a * b; }; caculateMap[4] = [](int a, int b)->int {return a / b; }; cout << "請選擇"; int choice; cin >> choice; cout << caculateMap[choice](10, 15) << endl; return 0; }
智能指針自定義刪除器:
#include<iostream> #include<vector> #include<functional> using namespace std; int main() { unique_ptr<FILE, function<void(FILE*)>> ptr1(fopen("data.txt", "w"), [](FILE* pf) {fclose(pf); }); }
傳入函數對象使得容器元素按照指定方式排列:
#include<iostream> #include<vector> #include<functional> #include <queue> using namespace std; class Data { public: Data(int val1=10,int val2=10):ma(val1),mb(val2){} int ma; int mb; }; int main() { //優先級隊列 //priority_queue<Data> queue; using FUNC = function<bool(Data&, Data&)>; priority_queue<Data, vector<Data>, FUNC> maxHeap([](Data& d1, Data& d2)->bool { return d1.mb > d2.mb; }); maxHeap.push(Data(10, 20)); maxHeap.push(Data(15, 15)); maxHeap.push(Data(20, 10)); }
到此這篇關於C++11中bind綁定器和function函數對象介紹的文章就介紹到這瞭,更多相關C++bind綁定器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!