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

数据挖掘

学习数据挖掘

 
 
 

日志

 
 

STL中的函数对象(Function Objects)  

2012-04-14 12:38:06|  分类: C++基本技巧 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
原文地址:http://apps.hi.baidu.com/share/detail/9270593
概要
一个函数对象(Function Object或者Functor)简单的说就是能够以函数调用的形式出现的任何东西。一个普通的函数显然就是一个函数对象,函数指针也是,更一般的,一个定义了operator()的Class也是。
基本的函数对象的概念有Generator,Unary Function(一元函数),Binary Function(二元函数):他们各自表示能以F(),F(x),F(x,y)的形式出现的函数对象。当让这些可以扩展为Ternary Funtion甚至更多,但是实际上没有哪个STL算法用到了两个以上参数的函数对象。其他所有STL定义的函数对象的感念都是这三个基本概念的细化(refinements)。
返回bool类型的函数对象是相当重要的一类函数对象。一个返回bool值的Unary Function称作Predicate(谓词),相应的返回bool值的Binary Function称作Binary Predicate(二元谓词)。
在Function Objects和Adaptable Function Objects(可适应的函数对象)之间有重要但是有些微妙的区别。一般地说,虽然一个Function Objects对它的参数的类型有要求,但是操作符operator()可以重载,可以是模板,或者两者兼有。也就是说,没有准确地方法获得这个Function Objects的参数和返回类型的信息。但是一个Adaptable Function Objects必须以typedef的形式指定他的参数和返回值得类型。比如,类型F0是Adaptable Function Objects的模型,那么必须定义F0::result_type。类似的,如果F1是Adaptable Unary Function Objects的模型,那么F1::argument_type和F1::result_type必须有定义;如果F2是Adaptable Binary Function Objects的模型,那么必须定义F2::first_argument_type,F2::second_argument_type和F2::result_type。STL提供了基类 unary_function和binary_function来简化Adaptable Unary Functions和Adaptable Binary Functions的模型的定义。

Adaptable Function Objects是非常重要的,因为他们可以被function object adaptors(函数对象适配器,用来操作和控制其它函数对象)使用。STL提供了许多function object adaptors,包括unary_negate,unary_compose和binary_compose,用来对函数对象进行组合。

最后,STL包括了许多不同的预定义的函数对象,包括算子(plus,minus,multiplies,divides,modulus和negate),算术比较(equal_to,not_equal_to,greater,less,greater_equal和less_equal),和逻辑操作(logical_and,logical_or和logical_not)。这样你就可以不用手动写新的函数对象而是用这些函数对象就可以组合出相当复杂的操作。

例子
将一个vector<int>用随机数填充。这里,一个函数指针就是一个函数对象。
    vector<int> V(100);
    generate(V.begin(), V.end(), rand);

按照绝对值大小排序一个vector<int>。这里,函数对象是一个用户定义的class类型。

    struct less_mag : public binary_function<double, double, bool> {
bool operator()(double x, double y) { return fabs(x) < fabs(y); }
    };

    vector<double> V;
    ...
    sort(V.begin(), V.end(), less_mag());

对一个vector<int>进行求和。这里,函数对象是一个可以保存状态的用户定义类型。

    struct adder : public unary_function<double, void>
    {
      adder() : sum(0) {}
      double sum;
      void operator()(double x) { sum += x; }
    };

    vector<double> V;
    ...
    adder result = for_each(V.begin(), V.end(), adder()); [3]
    cout << "The sum is " << result.sum << endl;

删除list<int>中所有大于100且小于1000的元素

    list<int> L;
    ...
    list<int>::iterator new_end = 
remove_if(L.begin(), L.end(),
     compose2(logical_and<bool>(),
       bind2nd(greater<int>(), 100),
       bind2nd(less<int>(), 1000)));
    L.erase(new_end, L.end());

=============================

函数对象的使用

利用C++标准模板库的算法可以为我们减轻许多负担,但这些算法大都需要函数或函数对象作为参数,比如用于排序的sort算法,它的接口定义如下:

template <class RandomAccessIterator>

void sort (RandomAccessIterator first,

           RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

void sort (RandomAccessIterator first,

           RandomAccessIterator last, Compare comp);

1种语法只适合对属于基本数据类型的对象排序,它使用<作为排序比较规则,第2种语法则是根据指定排序规则comp来进行排序,其中comp参数就是一个函数对象。如果要排序的对象不是如int,float等简单类型或不想使用<作为排序规则的话就得使用第2种语法,指定一个函数对象来作为排序规则(函数对象)进行排序,那函数对象究竟是什么了,其实简单地说它就是一些使用起来像调用函数一样的对象,如:一般的函数,函数指针,或重载了()运算符的类的实例等,使用这些对象的方式好像调用函数一样所以称这些对象为函数对象,如下就是函数对象定义:

//一般函数

void print(int x)

{

       if(x>=0&&x<=100) cout<<x<<” ”;

}

       //函数对象指针

typedef void(*ptPrint)(int x);

ptPrint prt=print;

//一个重载了()运算符得类

class isOk

{

public:

bool operator () (int val)

{

       if(val<0||val>100)

{

       return false;

}

return true;

}

};

有了上面得定义后,我们就可以以函数调用的形式使用上面的函数对象了,如: for_each(vectorA.begin(),vectorA.end(),print)打印大于等于0小于等于100的数, transform(vectorA.begin(),vectorA.end(),vectorB.begin(),isOk())判断向量vectorA中值x的范围是否在0100之间,如果在则向量vectorB对应的值为真,否则为假,其中vectorB中的数据类型为bool。在实际中往往会根据调用函数对象时参数的多少将函数对象分为三种形式,,无参函数对象,一元函数对象和二元函数对象,刚才的isOK()就是一个无参函数对象。无参函数对象与一元,二元函数对象有很大的区别,一元,二元函数对象的定义需要继承以下函数原型:

       //一元函数对象函数原型

template <class Arg, class Result>

struct unary_function {

typedef Arg argument_type;

typedef Result result_type;

};

       //二元函数对象函数原型

template <class Arg1, class Arg2, class Result>

struct binary_function {

typedef Arg1 first_argument_type;

typedef Arg2 second_argument_type;

typedef Result result_type;

};

Result为函数返回值,如果我们要定义一元或二元函数对象的话需要继承这两个函数对象的原型,如:

class gradeCompare : binary_function<Grade,Grade, bool> {

public:

bool operator () (Grade X,Grade Y) const

     {

        return X.Score>Y.Score;

     }

};

上面就是一个二元函数对象的类,该类的实例就是二元函数对象,考虑如下情况:      

有许多学生的成绩情况以对象的形式存在于一个向量vector中,现在我们需要对这些学生成绩进行排序,使用标准模板库的sort算法,学生成绩包含许多字段,如学号,姓名,成绩三个字段,成绩情况的对象定义如下:

class Grade

{

public:

    Grade(int id,string name,int score)

    {

        ID=id;

        Name=name;

        Score=score;

    }

    int ID;

    string Name;

    int Score;

};

根据sort算法的原型,我们就必须定义一个用于比较学生成绩的函数对象如下:

class gradeCompare : binary_function<Grade,Grade, bool> {

public:

bool operator () (Grade X,Grade Y) const

     {

        return X.Score>Y.Score;

     }

};

当定义了用于排序的函数对象后就可以直接调用sort算法进行排序了。

sort(finalGrade.begin(),finalGrade.end(),gradeCompare());//finalGrade是用来存储学生成绩的向量

整个源代码如下:

//---------------------------------------------------------------------------

#i nclude <vector>

#i nclude <string>

#i nclude <algorithm>

#i nclude <iostream>

using namespace std; //使用标准模板库的命名空间

//---------------------------------------------------------------------------

#pragma argsused

class Grade

{

public:

    Grade(int id,string name,int score)

    {

        ID=id;

        Name=name;

        Score=score;

    }

    int ID;

    string Name;

    int Score;

};

       //打印学生成绩

void printScore(Grade grade)

{

    cout<<grade.ID<<" "<<grade.Name<<" "<<grade.Score<<endl;

}

       //定义函数对象用来排序

class gradeCompare : binary_function<Grade,Grade, bool> {

public:

bool operator () (Grade X,Grade Y) const

     {

        return X.Score>Y.Score;

     }

};

int main(int argc, char* argv[])

{

    vector<Grade>finalGrade;

    finalGrade.push_back(Grade(1,"A",56));

    finalGrade.push_back(Grade(2,"B",57));

    finalGrade.push_back(Grade(3,"C",58));

   sort(finalGrade.begin(),finalGrade.end(),gradeCompare());

    for_each(finalGrade.begin(),finalGrade.end(),printScore);

    return 0;

}

结果如下:

3 C 58

2 B 57

1 A 56


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

历史上的今天

评论

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

页脚

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