-
仿函數
鎖定
- 中文名
- 仿函數
- 外文名
- functor
- 釋 義
- 使一個類的使用看上去象一個函數
- 性 質
- 編程術語
目錄
仿函數仿函數的概念與作用
有些時候,我們在寫代碼時會發現,某些功能實現的代碼會不斷的在不同的成員函數中用到,可又不好將這些代碼獨立出來成為類的一個成員函數,但又很想複用這些代碼。寫一個公共的函數是一個解決方法,不過函數用到的一些變量,就可能成為公共的全局變量。而且為了複用這麼一片代碼,就要單立出一個函數,也不好維護,這時就可以用仿函數了。寫一個簡單類,除了那些維護一個類的成員函數外,就只是實現一個operator(),在類實例化時,就將要用的,非參數的元素傳入類中。這樣就免去了對一些公共變量全局化的維護。同時,又可以使那些代碼獨立出來,以便下次複用。而且,這些仿函數還可以用關聯、聚合、依賴的類之間的關係,與用到他們的類組合在一起,這樣有利於資源的管理(這點可能是它相對於函數最顯著的優點了)。如果再配合上模板技術和policy編程思想,就更是威力無窮了,大家可以慢慢的體會。
有時仿函數的使用是為了函數擁有類的性質,以達到安全傳遞函數指針,依據函數生成對象,甚至是讓函數之間有繼承關係,對函數進行運算和操作的效果。比如set就使用了仿函數less ,而less繼承的binary_function,就可以看作是對於一類函數的總體聲明瞭,這是函數做不到的。
//less的定義 template<typename _Tp> struct less : public binary_function<_Tp, _Tp, bool> { bool operator()(const _Tp& __x, const _Tp& __y) const { return __x < __y; } };
//set的定義 template<typename _Key, typename _Compare = std::less<_Key>, typename _Alloc = std::allocator<_Key> > class set;
仿函數還給出了static的替代方案,函數內的靜態變量可以改成類的私有成員,這樣可以明確地在析構函數中清除所用的內容,如果用到了指針,那麼這個是不錯的選擇。有人説這樣的類已經不是仿函數了,但其實,封裝後從外界觀察,可以明顯地發現,它依然有函數的性質。
面向對象能夠減少代碼的耦合性,同樣仿函數也沾了class的光。比如,一個dfs()函數,調用的時候要傳入位置、深度兩個值。從外部觀察,dfs(x,1)的語句中,1的意義並不明確,從實際來講,也的確沒有傳入的必要,甚至可能導致錯誤。
一般的解決方案有這樣幾種:
1、void dfs(int x,int deep=1){...}這樣的話,雖然dfs(x)變成了可用語句,但你不能要求每個調用它的人都只傳一個參。如果有人寫dfs(x,-9999),可能會導致運行錯誤。
2、void dfs2(int x,int deep){}void dfs(int x){dfs2(x,1);}同樣dfs(x)也是可用的,但是如果另一個使用者並不知道dfs與dfs2的區別,寫了dfs2(x,-1)還是有風險
3、namespace 某個名字{void dfs2(int x,int deep){...}}void dfs(int x){某個名字::dfs2(x,1);}這樣已經不錯了,但是namespace的意義不明,其它使用者看到大綱估計會在心中把你千刀萬剮。
4、使用仿函數,把dfs()寫成仿函數,把2中的dfs2變成它的私有成員函數,這樣不會有意義不明的東西出現,也能實現安全調用,從外部看,這就是一個可以“生活自理”、有“獨立意識”函數了。
仿函數仿函數在各編程語言中的範例
仿函數C
#include <stdlib.h> /* Callback function */ int compare_ints_function(void*A,void*B) { return*((int*)(A))<*((int*)(B)); } /* Declaration of C sorting function */ void sort(void*first_item,size_t item_size,void*last_item,int(*cmpfunc)(void*,void*)); int main(void) { int items[]={4,3,1,2}; sort((void*)(items),sizeof(int),(void*)(items +3), compare_ints_function); return 0; }
仿函數C++
class compare_class{ public: bool operator()(int A, int B)const{return A < B;} }; // Declaration of C++ sorting function. template<class ComparisonFunctor> void sort_init(int *begin_items, int num_item, CompareFunctor c) { for(int i = 0; i < num_item; i++) { for(int j = i + 1; j < num_item; j++) { if( c(begin_items[i], begin_items[j]) ) { int temp = begin_items[i]; begin_items[i] = begin_items[j]; begin_items[j] = temp; } } } } int main(){ int items[]={4, 3, 1, 2}; compare_class functor; sort_ints(items, sizeof(items)/sizeof(items[0]), functor); }
仿函數C#
C#是通過委託(delegate)來實現仿函數的。
仿函數Java
Java中的仿函數是通過實現包含單個函數的接口實現的
List<String> list =Arrays.asList("10", "1", "20", "11", "21", "12"); Comparator<String> numStringComparator =new Comparator<String>(){ publicint compare(String o1, String o2){ returnInteger.valueOf(o1).compareTo(Integer.valueOf(o2)); } }; Collections.sort(list, numStringComparator);
仿函數仿函數的實際應用C++
#include <iostream> #include <algorithm> #include <cstdio> #include <ctime> #include <cstring> #include <string> #include <set> typedef long long LL; class Prt{ char c[53]; int top; public: template <class T> Prt& operator()(T x); Prt& operator()(LL x); Prt& operator()(int x); Prt& operator()(char x); Prt& operator()(const char*x); Prt& operator()(double x); Prt& operator()(const std::string x); Prt& operator()(double x,int k){ sprintf(c,"%%.%dlf",k); printf(c,x); return *this; } }; template <typename T> Prt& Prt::operator()(T x){ std::cout<<x; return *this; } Prt& Prt::operator()(LL x){ if(x==0){ putchar('0'); return *this; } if(x<0){ putchar('-'); x=-x; } top=0; while(x>0){ c[top++]='0'+x%10; x/=10; } while(top--){ putchar(c[top]); } return *this; } Prt& Prt::operator()(int x){ return operator()((LL)(x)); } Prt& Prt::operator()(char x){ putchar(x); return *this; } Prt& Prt::operator()(const char*x){ printf("%s",x); return *this; } Prt& Prt::operator()(double x){ printf("%lf",x); return *this; } Prt& Prt::operator()(const std::string x){ return operator()(x.data()); } Prt prt; struct st{int x,y;st(){x=y=0;}st(int a,int b){x=a;y=b;}}; std::ostream& operator<<(std::ostream& fout,const st&x){ fout<<"["<<x.x<<","<<x.y<<"]"; return fout; } int main(){ prt(">>> prt(\"LL:\")(12341231234123ll)(\' \')(\"int:\")(15)(\'\\n\');\n"); prt("LL:")(12341231234123ll)(' ')("int:")(15)('\n'); prt('\n'); prt(">>> prt(\"char:\")(\'a\')(\" char*(/string):\")(std::string(\"abc\"))(\" d \")\ ((std::string(\"abc\")).data())(\'\\n\');\n"); prt("char:")('a')(" char*(/string):")(std::string("abc"))(" d ") ((std::string("abc")).data())('\n'); prt('\n'); prt(">>> prt(\"double:\")(1.5)(\" double[int]:\")(10.12349746192736,4)(\'\\n\');\n"); prt("double:")(1.5)(" double[int]:")(10.12349746192736,4)('\n'); prt("\n>>> prt(st(12,31));\n"); prt(st(12,31)); prt('\n'); return 0; }
這裏放一下輸出,這樣的安排主要是為了突出代碼效果
>>> prt("LL:")(12341231234123ll)(' ')("int:")(15)('\n');
LL:12341231234123 int:15
>>> prt("char:")('a')(" char*(/string):")(std::string("abc"))(" d ")((std::string("abc")).data())('\n');
char:a char*(/string):abc d abc
>>> prt("double:")(1.5)(" double[int]:")(10.12349746192736,4)('\n');
double:1.500000 double[int]:10.1235
>>> prt(st(12,31));
[12,31]