仿函式

仿函式

仿函式(functor),就是使一個類的使用看上去像一個函式。其實現就是類中實現一個operator(),這個類就有了類似函式的行為,就是一個仿函式類了。

基本介紹

  • 中文名:仿函式
  • 外文名:functor
  • 釋義:使一個類的使用看上去象一個函式
  • 性質:編程術語
仿函式的概念與作用,仿函式在各程式語言中的範例,C,C++,C#,Java,仿函式的實際套用C++,

仿函式的概念與作用

在我們寫代碼時有時會發現有些功能實現的代碼,會不斷的在不同的成員函式中用到,但是又不好將這些代碼獨立出來成為一個類的一個成員函式。但是又很想復用這些代碼。寫一個公共的函式,可以,這是一個解決方法,不過函式用到的一些變數,就可能成為公共的全局變數,再說為了復用這么一片代碼,就要單立出一個函式,也不是很好維護。這時就可以用仿函式了,寫一個簡單類,除了那些維護一個類的成員函式外,就只是實現一個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

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++

在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_ints(int* begin_items, int num_items, ComparisonFunctor c);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]

相關詞條

熱門詞條

聯絡我們