注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

数据挖掘

学习数据挖掘

 
 
 

日志

 
 

STL之函数对象(-)  

2012-04-14 13:39:11|  分类: C++基本技巧 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

   

http://blog.csdn.net/shadow_gz/article/details/5935559

 STL中有很多算法函数(包含在algorithm)都需要提供一个称为函数对象(Function Object)类型的参数。为什么需要这样一个参数?

    首先这些算法通常是对容器(即存放一堆同类元素的对象,比如list、vector)内的诸多元素进行某种操作,比如sort是对该容器内的这些元素进行排序、find_if是查找该容器内符合某种条件的元素、count_if是计算该容器内符合某种条件的元素总数。

    为了算法可复用,其中的子操作应该是参数化的,比如sort的排序原则(顺序、逆序)、find_if/count_if的匹配条件。函数对象就是用来描述这些子操作的。

例S-1
//////////////////////////////////////////////////////////////////////////
 1 #include <iostream>
 2 #include <iterator>
 3 #include <algorithm>
 4 #include <functional>
 5
 6 using namespace std;
 7 void Add(int & ri)
 8 {
 9     ri += 5;
10 }
11 struct Sub : public unary_function<int,void>
12 {
13     void operator() (int & ri)
14     {
15         ri -= 5;
16     }
17 };
18
19 int main()
20 {
21     int a[10]={0,1,2,3,4,5,6,7,8,9};
22     copy(a,a+10,ostream_iterator<int>(cout," "));
23     cout << endl;
24     for_each(a,a+10,Add);
25     copy(a,a+10,ostream_iterator<int>(cout," "));
26     cout << endl;
27     for_each(a,a+10,Sub());
28     copy(a,a+10,ostream_iterator<int>(cout," "));
29     cout << endl;
30 }
//////////////////////////////////////////////////////////////////////////

    先看看for_each函数的原型:

template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator first, InputIterator last, UnaryFunction f);

    前两个参数分别说起始元素和终止元素的地址,第三个参数是一个一元函数。

    例S-1中,Add和Sub()都是函数对象,其中Add就是一个全局函数,在24行把Add作为for_each的参数。

    在27行,用Sub结构类型实例化一个函数对象传给for_each作参数。Sub从基类(事实上是个结构)unary_function派生,unary_function的定义如下:

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

    函数对象可以是一个普通函数、可以是函数指针,可以跟其对象一样是某个类的一个实例,但该类必须定义圆括号“()”运算符。

    函数对象描述的是一种操作,它也可以有参数,根据函数对象的参数个数,STL算法用到的主要有三类:

  1. 没有参数的函数对象(Generator),相当于“f()”,比如:
    vector<int> V(10);
    generate(V.begin(), V.end(), rand);
    其中的“rand”就是属于Generator。
  2. 一个参数的函数对象、一元函数(Unary Function),相当于“f(x)”,比如上面的“Add”,“Sub()”也返回一个一元函数对象。STL中用unary_function定义了一元函数基类。
  3. 两个参数的函数对象、二元函数(Binary Function),相当于“f(x,y)”,比如:
    struct less_mag : public binary_function<double, double, bool>
    {
        bool operator()(double x, double y) { return fabs(x) < fabs(y); }
    };
    binary_function定义了二元函数基类:
    template <class _Arg1, class _Arg2, class _Result>
    struct binary_function {
        typedef _Arg1 first_argument_type;
        typedef _Arg2 second_argument_type;
        typedef _Result result_type;
    };

    函数对象也有返回值,返回值是bool型的函数对象称谓词(Predicate),通常称返回bool的一元函数为谓词,返回bool的二元函数为二元谓词。

    有时候,需要把一个二元函数对象转换为一元函数对象、或者组合几个二元谓词为一个谓词。

例S-2
//////////////////////////////////////////////////////////////////////////
 1 #include <iostream>
 2 #include <iterator>
 3 #include <algorithm>
 4 #include <functional>
 5
 6 using namespace std;
 7 int Mul(int ri,int multiplier)
 8 {
 9     ri *= multiplier;
10     return ri;
11 }
12
13 int main()
14 {
15     int a[10]={0,1,2,3,4,5,6,7,8,9};
16     int b[10];
17     copy(b,b+10,ostream_iterator<int>(cout," "));
18     cout << endl;
19     transform(a,a+10,b,bind2nd(ptr_fun(Mul),5));
20     copy(b,b+10,ostream_iterator<int>(cout," "));
21     cout << endl;
22 }
//////////////////////////////////////////////////////////////////////////

    例S-2中,19行调用ptr_fun把一个普通全局函数转换成一个二元函数对象,并调用bind2nd函数把该二元函数对象的第二个参数multiplier绑定为5成为一个一元函数对象。

    例S-2就是把a[i]*5存入b[i]中,可以使用:

transform(a,a+10,b,bind2nd(ptr_fun(Mul),7));

    把a[i]*7存入b[i]中。

    ptr_fun有两个原型:
template <class Arg, class Result>
pointer_to_unary_function<Arg, Result> 
ptr_fun(Result (*x)(Arg));

template <class Arg1, class Arg2, class Result>
pointer_to_binary_function<Arg1, Arg2, Result> 
ptr_fun(Result (*x)(Arg1, Arg2));

    这里显然用到第二个原型:
template <class _Arg1, class _Arg2, class _Result>
inline pointer_to_binary_function<_Arg1,_Arg2,_Result> 
ptr_fun(_Result (*__x)(_Arg1, _Arg2)) {
    return pointer_to_binary_function<_Arg1,_Arg2,_Result>(__x);
}

    所以上面的语句完全可以写成:
transform(a,a+10,b,bind2nd(pointer_to_binary_function<int,int,int>(Mul),7));

    “pointer_to_binary_function<_Arg1,_Arg2,_Result>”是个模板类,它的构造函数以一个二元函数指针为参数:
template <class _Arg1, class _Arg2, class _Result>
class pointer_to_binary_function : public binary_function<_Arg1,_Arg2,_Result> {
protected:
    _Result (*_M_ptr)(_Arg1, _Arg2);
public:
    pointer_to_binary_function() {}
    explicit pointer_to_binary_function(_Result (*__x)(_Arg1, _Arg2)) : _M_ptr(__x) {}
    _Result operator()(_Arg1 __x, _Arg2 __y) const {
        return _M_ptr(__x, __y);
    }
};

    它从binary_function派生,有个保护成员变量_M_ptr保存二元函数指针,定义了“()”运算符,其中调用_M_ptr指向的二元函数。

    bind2nd也是个内联函数:
template <class _Operation, class _Tp>
inline binder2nd<_Operation> 
bind2nd(const _Operation& __fn, const _Tp& __x) 
{
    typedef typename _Operation::second_argument_type _Arg2_type;
    return binder2nd<_Operation>(__fn, _Arg2_type(__x));
}

    所以刚才的语句又可以写成:

transform(a,a+10,b,binder2nd<pointer_to_binary_function<int,int,int> >(pointer_to_binary_function<int,int,int>(Mul),5));

    “binder2nd<_Operation>”也是一个模板类,“_Operation”在这里被“pointer_to_binary_function<int,int,int>”替代,其构造函数有两个参数,第一个是_Operation类对象,第二个是_Operation的第二个参数类型常量:
template <class _Operation> 
class binder2nd : public unary_function<typename _Operation::first_argument_type, typename _Operation::result_type> {
protected:
    _Operation op;
    typename _Operation::second_argument_type value;
public:
    binder2nd(const _Operation& __x, const typename _Operation::second_argument_type& __y) : op(__x), value(__y) {}
    typename _Operation::result_type operator()(const typename _Operation::first_argument_type& __x) const {
        return op(__x, value); 
    }
};

    它从unary_function派生,也有个保护成员变量op保存实例化时传入的_Operation对象,并有保护成员变量value保存被绑定的第二个参数值,也定义了“()”运算符,其中调用二元函数对象op的“()”运算符。

    从“transform(a,a+10,b,bind2nd(ptr_fun(Mul),5));”到“transform(a,a+10,b,binder2nd<pointer_to_binary_function<int,int,int> >(pointer_to_binary_function<int,int,int>(Mul),5));”,虽然从语法上复杂了(事实上也没有人会这样写C++代码),但却弄清了函数对象的来龙去脉。

    它充分利用了C++的模板特性,当然也依靠了其它一些关键字,如typedef、typename。

    象pointer_to_binary_function、binder2nd这样的模板类比较特殊,它们对其它函数对象进行修改或组装生成一个新的函数对象。比如pointer_to_binary_function它把普通全局函数Mul改成一个二元函数对象,binder2nd把pointer_to_binary_function实例化出来的二元函数对象和一个常量5组装生成一个一元函数对象。这样的模板类在STL中被称为函数对象适配器(function object adaptors)。

  评论这张
 
阅读(161)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017