複製鏈接
請複製以下鏈接發送給好友

仿函數

鎖定
仿函數(functor),就是使一個類的使用看上去像一個函數。其實現就是類中實現一個operator(),這個類就有了類似函數的行為,就是一個仿函數類了。
中文名
仿函數
外文名
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

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_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]