http://blog.csdn.net/jnu_simba/article/details/9500219
一、函數對象
1、函數對象(function object)也稱為仿函數(functor)
2、一個行為類似函數的對象,它可以沒有參數,也可以帶有若干參數。
3、任何重載了調用運算符operator()的類的對象都滿足函數對象的特征
4、函數對象可以把它稱之為smart function。
5、STL中也定義了一些標準的函數對象,如果以功能劃分,可以分為算術運算、關系運算、邏輯運算三大類。為了調用這些標準函數對象,需要包含頭文件<functional>。
二、自定義函數對象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include?<iostream> using?namespace?std; class?CFunObj { public: ????void?operator()() ????{ ????????cout?<<?"hello,function?object!"?<<?endl; ????} }; int?main() { ????CFunObj?fo; ????fo(); ????CFunObj()(); ????return?0; } |
注意:CFunObj()(); 表示先構造一個匿名對象,再調用operator();
三、函數對象與容器
在這邊舉map 容器的例子,大家都知道map 在插入元素的時候會自動排序,默認是根據key 從小到大排序,看map 的定義:
1 2 3 4 5 6 7 8 9 10 | //?TEMPLATE?CLASS?map template?<?class?_Kty, ?????????class?_Ty, ?????????class?_Pr?=?less<_Kty>, ?????????class?_Alloc?=?allocator<pair<const?_Kty,?_Ty>?>?> class?map ????:?public?_Tree<_Tmap_traits<_Kty,?_Ty,?_Pr,?_Alloc,?false>?> { ????//?ordered?red-black?tree?of?{key,?mapped}?values,?unique?keys }; |
假設現在我們這樣使用 map< int, string > mapTest; 那么默認的第三個參數 _Pr = less<int>,再者,map 繼承的其中一個類
?_Tmap_traits 中有個成員:
?_Pr ?comp;// the comparator predicate for keys?
跟蹤進insert 函數,其中有這樣一句:
if (_DEBUG_LT_PRED(this->comp,?_Key(_Where._Mynode()), this->_Kfn(_Val)))
已知?#define _DEBUG_LT_PRED(pred, x, y) pred(x, y) 很明顯地,comp 在這里當作函數對象使用,傳入兩個參數,回頭看less 類的
模板實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 | //?TEMPLATE?STRUCT?less template<class?_Ty> struct?less ????????:?public?binary_function<_Ty,?_Ty,?bool> { ????//?functor?for?operator< ????bool?operator()(const?_Ty?&_Left,?const?_Ty?&_Right)?const ????{ ????????//?apply?operator<?to?operands ????????return?(_Left?<?_Right); ????} }; |
即實現了operator() 函數,左操作數小于右操作數時返回為真。
我們也可以在定義的時候傳遞第三個參數,如map< int, string, greater<int> > mapTest; 則插入時按key 值從大到小排序(less,
?greater 都是STL內置的類,里面實現了operator() 函數),甚至也可以自己實現一個類傳遞進去,如下面例程所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include?<map> #include?<string> #include?<iostream> using?namespace?std; struct?MyGreater { ????bool?operator()(int?left,?int?right) ????{ ????????return?left?>?right; ????} }; int?main(void) { ????map?<?int,?string,?/*greater<int>?*/MyGreater?>?mapTest; ????mapTest.insert(map<int,?string>::value_type(1,?"aaaa")); ????mapTest.insert(map<int,?string>::value_type(3,?"cccc")); ????mapTest.insert(map<int,?string>::value_type(2,?"bbbb")); ????for?(map?<?int,?string,?/*greater<int>?*/MyGreater?>::const_iterator?it?=?mapTest.begin();?it?!=?mapTest.end();?++it) ????{ ????????cout?<<?it->first?<<?"?"?<<?it->second?<<?endl; ????} ????return?0; } |
輸出為:
3 cccc
2 bbbb
1 aaaa
MyGreater 類并不是以模板實現,只是比較key 值為int 類型的大小。
四、函數對象與算法
在STL一些算法中可以傳入函數指針,實現自定義比較邏輯或者計算,同樣地這些函數也可以使用函數對象來代替,直接看例程再稍
作分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | #include?<vector> #include?<string> #include?<iostream> #include?<algorithm> using?namespace?std; void?PrintFun(int?n) { ????cout?<<?n?<<?'?'; } void?Add3(int?&n) { ????n?+=?3; } class?PrintObj { public: ????void?operator()(int?n) ????{ ????????cout?<<?n?<<?'?'; ????} }; class?AddObj { public: ????AddObj(int?number)?:?number_(number) ????{ ????} ????void?operator()(int?&n) ????{ ????????n?+=?number_; ????} private: ????int?number_; }; class?GreaterObj { public: ????GreaterObj(int?number)?:?number_(number) ????{ ????} ????bool?operator()(int?n) ????{ ????????return?n?>?number_; ????} private: ????int?number_; }; int?main(void) { ????int?a[]?=?{1,?2,?3,?4,?5}; ????vector<int>?v(a,?a?+?5); ????/*for_each(v.begin(),?v.end(),?PrintFun); ????cout<<endl;*/ ????for_each(v.begin(),?v.end(),?PrintObj()); ????cout?<<?endl; ????/*for_each(v.begin(),?v.end(),?Add3); ????for_each(v.begin(),?v.end(),?PrintFun); ????cout<<endl;*/ ????for_each(v.begin(),?v.end(),?AddObj(5)); ????for_each(v.begin(),?v.end(),?PrintFun); ????cout?<<?endl; ????cout?<<?count_if(a,?a?+?5,?GreaterObj(3))?<<?endl;?//計算大于3的元素個數 ????return?0; } |
輸出為:
1 2 3 4 5
6 7 8 9 10
2
回顧for_each 的源碼,其中有這樣一句:?_Func(*_ChkFirst); 也就是將遍歷得到的元素當作參數傳入函數。
上面程序使用了函數對象,實際上可以這樣理解?PrintObj()(*_ChkFirst); 即?PrintObj() 是一個匿名的函數對象,傳入參
數,調用了operator() 函數進行打印輸出。使用函數對象的好處是比較靈活,比如直接使用函數Add3,那么只能將元素加3,而
使用函數對象Addobj(x), 想讓元素加上多少就傳遞給Addobj類,構造一個對象即可,因為它可以保存一種狀態(類成員)。
count_if 中的 GreaterObj(3) 就類似了,將遍歷的元素當作參數傳遞給operator(), 即若元素比3大則返回為真。
五、STL內置的函數對象類
參考:
C++ primer 第四版
Effective C++ 3rd
C++編程規范