函数对象

来源:互联网 发布:商铺电负荷计算软件 编辑:程序博客网 时间:2024/06/04 00:33

为什么要写函数对象这篇博客呢?主要原因就是函数对象在STL中用得出神入化,给人一种神来之笔的感觉。不知道你是不是也有同感呢?

学习某个新东西,肯定是这个东西有用,我们有需求。所以在学习的时候,我喜欢把一个东西的来龙去脉全搞清楚。知其然,也要知其所以然。

废话不说,切入正题。

许多算法只使用一些迭代器和一些值在序列上的操作。我们来看下面的代码:在序列中找到第一个值为7的元素

void f(list<int> s){      list<int>::iterator p = find(s.begin(),s.end(),7);      //..........}


假如我们想要代码去执行找到第一个小于7的元素,该怎么办呢?我们会希望算法去执行我们提供的一些代码。


bool less_than_7(int v){      return v<7;}void f(list<int> &c){      list<int>::iterator p = find_if(c.begin(),c.end(),less_than_7);}

存在许多需要函数作为参数传递的使用方法,如:逻辑谓词,算术运算等等。为这类使用中的每一种写一个独立的函数,既不方便,也不是很有效。在很多情况下,对每个元素作用的函数还需要再调用之间保存一些数据,并将其作为许多这类应用的最后结果。与独立函数相比,类成员函数能更好地服务于这种需要。

下面一个拟函数的类,去完成一些求和工作:

template<class T>class Sum{      T res;public:      Sum(T i=0):res(i){}      void operator()(T x){res = x;}      T result()const{return res;}};

显然,Sum是为了那些用0初始化,并定义有+=的算法类型设计的。

void f(list<double> &ld){     Sum<double> s;     s = for_each(ld.begin(),ld.end(),s);     cout<<"the Sum is "<<s.result()<<'\n';}//我们可以猜测下,for_each()中大概是这样子运行for_each(.....){       while(ld.begin()!=ld.end()){              s(*ld.begin());              ++ld.begin();       }       //..........}

具有适当定义的对象也能很多的(通常是更好地)作为一个函数使用。例如,将一个类的应用运算符在

线化,比将一个指针传递的函数在线化容易的多。因此函数对象比常规函数执行速度更快。

标准库中提供了许多很有用的函数对象的基类:

template<class Arg,class Res>struct unary_function{       typedef Arg argument_type;       typedef Res result_type;};
template<class Arg,class Arg2,class Res>struct binary_function{       typedef Arg first_argument_type;       typedef Arg2 second_argument_type;       typedef Res result_type; };

我们可以利用这些基类派生出自己的类。例如:

template<class T>struct logical_not:public unary_function<T,bool>{    bool operator()(const T&x)const{return !x;}};template<class T>struct less:public binary_function<T,T,bool>{      bool operator()(const T&x,const T&y)const{return x<y;}};

所谓谓词就是想上面这种返回类型为bool的函数对象。具体用法如下:

void f(vector<int>&vi,list<int>&li){     typedef list<int>::iterator LI;     typedef vector<int>::iterator VI;     pair<VI,LI> p1 = mismatch(vi.begin(),vi.end(),li.begin(),less<int>());     //..........}

在函数mismatch中,less就可以像普通带两个参数的函数一样使用。

接下来,讨论下如何定义自己的能接受函数对象的函数:

在开始之前,我们先来看看STL中for_earch()与find_if()的定义:

template<class _InIt,    class _Fn1> inline    _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)    {    // perform function for each element    _DEBUG_RANGE(_First, _Last);    _DEBUG_POINTER(_Func);    _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));    _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));    for (; _ChkFirst != _ChkLast; ++_ChkFirst)        _Func(*_ChkFirst);    return (_Func);  }


貌似我们关注的重点在红色部分(额....不给显示红色,应该能看出来我说的红色部分在哪),其他部分是

些安全检查之类操作。

我们再来看看find_if()的源码:

template<typename _InputIterator, typename _Predicate>    inline _InputIterator    find_if(_InputIterator __first, _InputIterator __last,        _Predicate __pred)    {      // concept requirements      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)      __glibcxx_function_requires(_UnaryPredicateConcept<_Predicate,          typename iterator_traits<_InputIterator>::value_type>)      __glibcxx_requires_valid_range(__first, __last);      return std::__find_if(__first, __last, __pred,                std::__iterator_category(__first));    }   /// This is an overload used by find_if() for the Input Iterator case.  template<typename _InputIterator, typename _Predicate>    inline _InputIterator    __find_if(_InputIterator __first, _InputIterator __last,          _Predicate __pred, input_iterator_tag)    {      while (__first != __last && !bool(__pred(*__first)))    ++__first;      return __first;    }


认真分析源码,向大师学习。

那么我们先来定义自己的函数。

template<class _Iter1,class _Iter2,class _Fcn>inline 
_Iter1 get_first_less(_Iter1 first,_Iter1 last,_Iter2 first2,_Fcn f){      while(first!=last){            if(f(*first,*first2))return first;      }      return last;}

在使用的时候,这样:

vector<int>::iterator p1 = get_first_less(vin1.begin(),vin1.end(),vin2.begin(),less_than<int>);

下面我们来看看成员函数适配器

当我们写的函数模板的参数为成员函数的时候,我们该怎么办呢?例如:

void draw_all(list<Shap *>&c){     for_each(c.begin(),c.end(),&shap::draw);  // 肯定出错}

成员函数总是需要一个对象去调用它:p->draw(),静态成员函数除外。这个时候我们总需要有一种方便而有效的方式

去建立某种东西,使一个算法能够调用成员函数。

我们先来定义个自己的函数对象。

template<class R,class T>class Mem_fun_t:public unary_function<T*,R>{      R(T::*pmf)();public:      explicit Mem_fun_t(R(T::*p)()):pmf(p){}      R operator()(T *p)const{             return (p->*pmf)();      }}template<class R,class T>Mem_fun_t<R,T>Mem_fun(R(T::*f)()){       return Mem_fun_t<R,T>(f);}

这样我们就可以处理Shape::draw()了。


void draw_all(list<Shape*>&lsp){ for_each(lsp.begin(),lsp.end(),Mem_fun(&Shape::draw));}


如果没看懂,我再啰嗦下。看懂的跳过此段。当我们遇到这种情况时,一般会想到什么样的办法呢?通过成员函数指针
来调用成员函数。返回类型必须是类的成员函数。这就是Mem_fun_t<R,T> Mem_fun(R(T::*f)())。其参数为R(T::*f)()
成员函数指针,返回值为Mem_fun_t<R,T>成员函数。要想做到Mem_fun_t<R,T>可以像函数一样调用,需要在其中加入一
个operator()(T *p)。Mem_fun_t的构造函数的参数必须是类的成员函数。
标准库不需要处理多于一个参数的成员函数,因为没有一个标准库算法以多于两个参数的函数为操作数。
至于函数参数为函数指针的情况,它的定义与实现很像成员适配器,我们只须将成员函数指针改为普通函数指针即可。
下面我们以一个例子结束。(若还有什么疑问,答案尽在例子中)
//为了大家的阅读方便,我将测试部分与函数实现部分写在一块儿#include<iostream>#include<vector>#include<algorithm>using std::for_each;using std::vector;using std::cout;using std::endl;using std::unary_function;using std::mem_fun;template<class T,class R>class mem_fun_t:public unary_function<T*,R>{R(T::*pmf)();public:explicit mem_fun_t(R(T::*p)()):pmf(p){}R operator()(T *p){return (p->*pmf)();}};template<class T,class R>mem_fun_t<T,R> mem_fun_my(R(T::*p)()){return mem_fun_t<T,R>(p);}template<class T>class Check{T num;public:Check(T tmp):num(tmp){}bool is_positive(){return num? 1:0;}};template<class _Iter,class _fun>_Iter check_each(_Iter first,_Iter last,_fun f){while(first != last){if(!f(*first)){cout<<"find ...."<<endl;return first;}++first;}return last;}void test1(){  //类的成员函数做参数vector<Check<int>*> vecI;vecI.push_back(new Check<int>(1));vecI.push_back(new Check<int>(2));vecI.push_back(new Check<int>(3));vecI.push_back(new Check<int>(0));vector<Check<int>*>::iterator p1 = check_each(vecI.begin(),vecI.end(),mem_fun_my(&Check<int>::is_positive));}template<class T>class D{T value;public:D(T a=0):value(a){}T get_value(){return value;}T operator *(){return value;}};template<class T>class D_fun{public:bool operator()(T *a){if(**a)return 1;return 0;}bool is_positive(T *a){if(**a)return 1;return 0;}};void test2(){    //仿函数做参数vector<D<int> *> vecD;vecD.push_back(new D<int>(1));vecD.push_back(new D<int>(2));vecD.push_back(new D<int>(3));vecD.push_back(new D<int>());vector<D<int> *>::iterator p1 = check_each(vecD.begin(),vecD.end(),D_fun<D<int>>());//vector<D<int> *>::iterator p1 =//check_erach(vecD.begin(),vecD.end(),mem_fun_my(&D_fun<D<int>>::is_positive));//错误,必须有对象,才能调用成员函数} //template<class T>typedef bool (*fun_ptr)(D<int> *);//这里不能用指向模板函数的模板指针,因为这样做指针的类型不能确定  故编译时候会出错//上述想法行不通,下面是改进方案。如果实在想做一个函数指针模板,我们可以这样...template<class T>struct Fun_ptr_struct{  //这样就不会说指针类型不确定了,因为类的参数类型定下来了,指针的类型也跟着定下来了。typedef bool(*fun_ptr)(T *);};template<class T>bool is_positive(T *a){if(**a)return 1;return 0;}void test3(){  //函数指针做参数vector<D<int> *> vecD;vecD.push_back(new D<int>(1));vecD.push_back(new D<int>(2));vecD.push_back(new D<int>(3));vecD.push_back(new D<int>());fun_ptr fp;fp = is_positive<D<int>>;vector<D<int> *>::iterator p1=check_each(vecD.begin(),vecD.end(),fp);Fun_ptr_struct<D<int>>::fun_ptr fp2;fp2 = is_positive<D<int>> ;vector<D<int> *>::iterator p2=check_each(vecD.begin(),vecD.end(),fp2);}//一个有意思的东西,让我们的函数对象用起来更精彩class Base{public:virtual void do_something( )=0;};class Derive1:public Base{public:void do_something(){cout<<"I am Derive1."<<endl;}};class Derive2:public Base{public:void do_something(){cout<<"I am Derive2."<<endl;}};void test4(){vector<Base *> vecB;vecB.push_back(new Derive1);vecB.push_back(new Derive2);for_each(vecB.begin(),vecB.end(),mem_fun(&Base::do_something));}int main(){test1();test2();test3();test4();}

0 0
原创粉丝点击