仿函数及其应用

仿函数(functor)

仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个 operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

仿函数的概念与作用

在我们写 代码时有时会发现有些功能实现的代码,会不断的在不同的成员函数中用到,但是又不好将这些代码独立出来成为一个类的一个成员函数。但是又很想复用这些代码。写一个公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的 全局变量,再说为了复用这么一片代码,就要单立出一个函数,也不是很好维护。这时就可以用仿函数了,写一个简单类,除了那些维护一个 类的成员函数外,就只是实现一个 operator(),在类实例化时,就将要用的,非参数的元素传入类中。这样就免去了对一些公共变量的全局化的维护了。又可以使那些代码独立出来,以便下次复用。而且这些仿函数,还可以用关联,聚合,依赖的类之间的关系,与用到他们的类组合在一起,这样有利于资源的管理(这点可能是它相对于函数最显著的优点了)。如果在配合上模板技术和policy编程思想,那就更是威力无穷了,大家可以慢慢的体会。
有时仿函数的使用是为了函数拥有类的性质,以达到安全传递函数指针,依据函数生成对象,甚至是让函数之间有继承关系,对函数进行运算和操作的效果。比如set就使用了仿函数less ,而less继承的binary_function,就可以看作是对于一类函数的总体声明了,这是函数做不到的。
1
2
3
4
5
6
7
8
9
//less的定义
 
template < typename  _Tp>
     struct  less :  public  binary_function<_Tp, _Tp,  bool >
     {
       bool
       operator()( const  _Tp& __x,  const  _Tp& __y)  const
       return  __x < __y; }
     };
1
2
3
4
//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++

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

猜你喜欢

转载自www.cnblogs.com/xietianjiao/p/12344086.html
今日推荐