何为C++仿函数(或者函数子)

仿函数,或者叫做函数子(functor)是C++中一个比较高深的概念,不过它的存在又非常广泛,如果你经常用到STL,你会发现它几乎无处不在。

在C++标准算法函数库里,也就是<algorithm>这个头文件里(其实是<bits/stl_algo.h>文件),几乎所有算法函数都会提供两个版本,比如

template<typename _RandomAccessIterator>
inline void
sort(_RandomAccessIterator __first, _RandomAccessIterator __last)

template<typename _RandomAccessIterator, typename _Compare>  
inline void
sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)

即一个版本使用默认的比较方式,另一个则是让用户自己定义比较规则,下面用到的for_each也是一样有两个,比较规则就是通过函数子来定制并传入的,这里的__comp就是需要用户传入一个比较函数子(comparison functor)

其实所谓的函数子,或者说是仿函数就是一个函数,只是用类的外壳进行了一次封装罢了。不封装也是完全可以的。看一面的例子吧!

#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
using namespace std;

void output(int __n)
{
    cout<<__n<<" ";
}

int main()
{
    vector<int> v(5,100);//新建一个vector,其中有5个元素,初始值都是100
    for_each(v.begin(),v.end(),output);//将vector里的元素依次输出
    return 0;
}

这里你会奇怪为什么使用一个函数指针也可以呢?让我们先看看for_each的函数实现

template<typename _InputIterator, typename _Function>
_Function
for_each(_InputIterator __first, _InputIterator __last, _Function __f)
{
    for (; __first != __last; ++__first)
            __f(*__first);
    return __f;
}

你惊奇的发现对函数子的使用就像使用函数指针一样__f(*__first);所以上面将一个函数指针当成函数子传递进去当然没有问题。那么标准C++希望我们如何来使用for_each函数呢?请看下面改写后的代码。

#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
using namespace std;

struct output : public unary_function<int,void>
{
    void operator() (int __n)
    {
        cout<<__n<<" ";
    }
};
int main()
{
    vector<int> v(5,100);//初始化vector,5个元素,元素值均为100
    //依次输出vector的元素,这里传入的不是函数指针,而是一个类
    for_each(v.begin(),v.end(),output());
    return 0;
}

这里我们真正地传了一个函数子,output()即是创建一个output类,确切地说这里是一个结构体,这里只是为了与C++标准风格一致,待会你就会看到C++的风格了,况且结构体与类的差别本来就不大。

从上面的代码你可能就明白了,所谓的函数子就是一个类,这个类有一个重载()操作符的成员函数,重载了操作符()就有一个很奇怪的特性了,可以像函数一样来使用,比如你可以使用output()(99);来输出99到屏幕。

你很奇怪为什么unary_function到底是干什么的,为什么一定要从它那里继承而来呢?看一下unary_function的实现你就会明白了,实现在<functional>头文件里(其实是在<bits/stl_function.h>

template<typename _Arg, typename _Result>
struct unary_function
{
    typedef _Arg argument_type;
    typedef _Result result_type;
};

其实unary_function只是重命名了类型罢了,什么也没有做,这么看来,其实你完全没有必要继承于它,不过标准C++都推荐你这么做。注意这里使用是的结构体,这也是我上面提到的标准风格,毕竟仿函数在概念上更偏向于函数,而不是类。

上面是一个为单目函数子准备的,所谓单目就是指该仿函数只接受一个参数,下面是标准库提供的一个双目函数子的父类,也是什么都不干的空壳。

template<typename _Arg1, typename _Arg2, typename _Result>
struct binary_function
{
    typedef _Arg1 first_argument_type;
    typedef _Arg2 second_argument_type;
    typedef _Result result_type;
};

里已经为你实现了很多与比较大小相关的函数子,比如

template<typename _Tp>
struct greater : public binary_function<_Tp, _Tp, bool>
{
    bool operator()(const _Tp& __x, const _Tp& __y) const
    { return __x > __y; }
};
template<typename _Tp>
struct equal_to : public binary_function<_Tp, _Tp, bool>
{
    bool operator()(const _Tp& __x, const _Tp& __y) const
    { return __x == __y; }
};

总之,仿函数在实现上就是从unary_function或者binary_function继承而来的具有operator()成员函数的类,而在概念上就是函数指针的功能,感觉标准之所以这样做是为了使用C++更OO(面向对象)一些。使用仿函数可以通过给仿函数添加成员函数与成员变量来完全复杂的功能,而一个纯粹的函数要完成同样复杂的功能可能不得不使用全局变量,并调用其他函数,导致结构上不清晰简洁,这样来看的话,仿函数尽管写起来有点复杂,但还是有其优势的。

发表于 2010年06月07日 05:12   评论:0   阅读:2669  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo