提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的基本算法,迭代器是由容器提供的一种接口,算法通过迭代器来操控容器。接下来要介绍的是另外的一种组件,函数对象(Function Object,JJHou译作Functor仿函数)。

什么是函数对象

  顾名思义,函数对象首先是一个对象,即某个类的实例。其次,函数对象的行为和函数一致,即是说可以像调用函数一样来使用函数对象,如参数传递、返回值等。这种行为是通过重载类的()操作符来实现的,举例说明之,

 
  1. class Print
  2. {
  3. public:
  4. void operator()(int n)
  5. {
  6. std::cout<<n<<std::endl;
  7. return ;
  8. }
  9. };
  10. int
  11. main(int argc, char **argv)
  12. {
  13. Print print;
  14. print(372);
  15. print.operator()(372); //~ 显式调用
  16. return 0;
  17. }

  其实我们早就开始使用函数对象了,当你写下sort(v.begin(), v.end())时(假定v是vector<int>),其实调用的是sort(v.begin(), v.end(), less<int>()),这样sort就会将v从小至大排序。若要逆向排序,你就需要显式地为sort指定一个排序规则,即函数对象greater<int>(). less<T>和greater<T>是STL中的两个模板类,它们使用类型T的<和>操作符。less<T>的一个典型实现可能是这样的:

   
  1. template <class T>
  2. class less
  3. {
  4. public:
  5. bool operator()(const T&l, const T&r)const
  6. {
  7. return l < r;
  8. }
  9. };

函数对象的分类

  根据用途和参数特征,STL中的函数对象通常分为以下几类:Predicates, Arithmetic Function Objects, Binders, Negaters, Member Function Adapters, Pointer to Function Adapters。下面逐一介绍一下,之前得先介绍两个基类:

 
  1. template<class Arg, class Res>
  2. struct unary_function //~ 一元函数对象基类
  3. {
  4. typedef Arg argument_type;
  5. typedef Res result_type;
  6. };
  7.  
  8. template<class Arg1, class Arg2, class Res>
  9. struct binary_function //~ 二元函数对象基类
  10. {
  11. typedef Arg1 first_argument_type;
  12. typedef Arg2 second_argument_type;
  13. typedef Res result_type;
  14. };

使用这两个基类,首先需要包含头文件。

Predicates

  Predicate是一种函数对象,返回值(应该是operator()的返回值)为布尔型,接受一个或者两个参数。通常用来判断对象的有效性(一个参数时)或者对两个对象进行比较(如less)。你可以根据自己的需要定义自己的Predicate,但STL已经定义了一些Predicate,你可以直接使用。

Predicates
Predicate 类型 描述
equal_to() Binary 使用==判等
not_equal_to() Binary 使用!=判等
less() Binary 使用<
greater() Binary 使用>
less_equal() Binary 使用<=
greater_equal() Binary 使用>=
logical_not() Unary 使用!逻辑取反
logical_and() Binary 使用&&逻辑与
logical_or() Binary 使用||逻辑或
算术运算函数对象

  进行简单的算术运算,这类函数对象我用的很少,通常是自己定义。

算术运算函数对象
函数对象 类型 描述
negate() Unary 使用-求负
plus() Binary 使用+加法
minus() Binary 使用-减法
multiplies() Binary 使用*乘法
divides() Binary 使用/除法
modulus() Binary 使用%求余
绑定Binders

  有两种绑定bind1st和bind2nd,它们可以将一个二元函数对象的其中一个参数绑定为某个已知的对象,从而得到一个一元函数对象。例如要在vector<int> v中查找等于372的值的位置,我可以将372绑定到equal_to<int>()的第一个或者第二个参数:

 
  1. int
  2. main(int argc, char **argv)
  3. {
  4. vector<int> v;
  5. for(int i = 0; i < 1000; ++i)
  6. {
  7. v.push_back(i);
  8. }
  9. vector<int>::iterator it;
  10. it = find_if(v.begin(), v.end(), bind1st(equal_to<int>(), 372));
  11. std::cout<<*it<<std::endl;
  12. return 0;
  13. }

  其实,这里的bind1st和bind2nd并不是函数对象,只是模板函数而已。这两个函数分别返回类型为binder1st和binder2nd的函数对象。下面的代码,聪明的你肯定一看就懂:

 
  1. // bind1st
  2. template<class Op>
  3. class binder1st : public unary_function
  4. <typename Op::second_argument_type,
  5. typename Op::result_type>
  6. {
  7. Op op_;
  8. typename Op::first_argument_type first_arg_;
  9.  
  10. public:
  11. binder1st(const Op& op,
  12. const typename Op::first_argument_type&
  13. first_arg) : op_(op),
  14. first_arg_(first_arg) {}
  15.  
  16. typename Op::result_type operator()
  17. (const typename Op::second_argument_type& arg) const
  18. {
  19. return op_(first_arg_, arg);
  20. }
  21. };
  22.  
  23. template<class Op, class Arg>
  24. inline binder1st<Op> bind1st(const Op& op,
  25. const Arg& arg)
  26. {
  27. return binder1st<Op>(op, arg);
  28. }
  29.  
  30. // bind2nd
  31. template<class Op>
  32. class binder2nd : public unary_function
  33. <typename Op::first_argument_type,
  34. typename Op::result_type>
  35. {
  36. Op op_;
  37. typename Op::second_argument_type second_arg_;
  38.  
  39. public:
  40. binder2nd(const Op& op,
  41. const typename Op::second_argument_type&
  42. second_arg) : op_(op),
  43. second_arg_(second_arg) {}
  44.  
  45. typename Op::result_type operator()(const typename
  46. Op::argument_type& arg) const
  47. {
  48. return op_(arg, second_arg_);
  49. }
  50. };
  51.  
  52. template<class Op, class Arg>
  53. inline binder2nd<Op> bind2nd(const Op& op,
  54. const Arg& arg)
  55. {
  56. return binder2nd<Op>(op, arg);
  57. }
Negaters

  Negater是针对Predicate设计的,它简单的将Predicate的返回值取反。有两个Negater,not1和not2,分别对一元和二元Predicate取反。

Member Function Adapters

  有时候,你可能想让算法调用容器元素的成员函数,而不是外部函数。因为外部无法改变对象内的状态,且内部函数通常具有更高的效率。例如swap(string, string)总是没有string.swap(string)快速。又比如sort无法对list进行排序,这时候只能使用list.sort()来给链表排序。这时候就需要使用一定的方法将对象内部的函数“变成”函数对象,这样的函数对象叫做成员函数适配器,其实前面的binder也是一种适配器。看下面的例子:

 
  1. int
  2. main(int argc, char **argv)
  3. {
  4. vector<list<int> > v;
  5. v.push_back(list<int>());
  6. vector<list<int> >::iterator it;
  7. for(it = v.begin(); it != v.end(); ++it)
  8. {
  9. for(int i = 0; i < 20; ++i)
  10. {
  11. (*it).insert((*it).begin(), i);
  12. }
  13. }
  14. for_each(v.begin(), v.end(), std::mem_fun_ref(&list<int>::sort));
  15. for(it = v.begin(); it != v.end(); ++it)
  16. {
  17. for(list<int>::iterator lt; lt != (*it).end(); ++lt)
  18. {
  19. std::cout<<*lt<<std::endl;
  20. }
  21. }
  22. return 0;
  23. }

上面的例子中,遍历vector<list<int> >并对链表进行排序。其中使用的是成员函数适配器mem_fun_ref,它返回的函数对象会以list<int>对象的引用为参数。另外一个mem_fun则是以指向list<int>对象的指针为参数。

from:http://www.cnblogs.com/weiqubo/archive/2011/02/16/1956552.html

仿函数(二、stl中常用仿函数)的更多相关文章

  1. STL中常用容器及操作 学习笔记1

    @[TOC](下面介绍STL中常见的容器及操作)## 不定长数组 vector> vetcor:其实就是一个数组或者说是容器 其操作不同于之前直接定义的数组 > 而且可以直接赋值也可以直接 ...

  2. STL中常用的c++语法

    函数调用操作(c++语法中的左右小括号)可以被重载,STL的特殊版本都以仿函数形式呈现.如果对某个class进行operator()重载,它就成为一个仿函数. 仿函数(functor),就是使一个类的 ...

  3. STL中常用算法

    一.排序 sort sort(first_pointer,first_pointer+n,cmp) 默认为升序 若要使用降序,自行写cmp 函数 bool cmp(int a,int b){ retu ...

  4. stl中常用的排序算法

    #include"iostream" #include"vector" using namespace std; #include"string&qu ...

  5. STL标准库-仿函数与仿函数适配器

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 概要: 1.仿函数 2.bind2nd() 3.not1() 4.bind() 仿函数的实现:声明一个类,重载它的operato ...

  6. C++ STL中的常用容器浅谈

    STL是C/C++开发中一个非常重要的模板,而其中定义的各种容器也是非常方便我们大家使用.下面,我们就浅谈某些常用的容器.这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中 ...

  7. 转:用STL中的vector动态开辟二维数组

    用STL中的vector动态开辟二维数组 源代码:#include <iostream>#include <vector>using namespace std;int mai ...

  8. 记录下项目中常用到的JavaScript/JQuery代码二(大量实例)

    记录下项目中常用到的JavaScript/JQuery代码一(大量实例) 1.input输入框监听变化 <input type="text" style="widt ...

  9. python中常用的模块二

    一.序列化 指:在我们存储数据的时候,需要对我们的对象进行处理,把对象处理成方便存储和传输的数据格式,这个就是序列化, 不同的序列化结果不同,但目的是一样的,都是为了存储和传输. 一,pickle.可 ...

随机推荐

  1. 【vs2013】使用VS2013打包程序

    如何用 VS 2013 打包 程序? 摘自:http://www.zhihu.com/question/25415940 更多请见摘自. 答案就在这里,想要你的exe独立运行在XP中:1.将平台工具集 ...

  2. HDU - 5289:Assignment(单调队列||二分+RMQ||二分+线段树)

    Tom owns a company and he is the boss. There are n staffs which are numbered from 1 to n in this com ...

  3. bzoj 1220 跳蚤

    Written with StackEdit. Description \(Z\)城市居住着很多只跳蚤.在\(Z\)城市周六生活频道有一个娱乐节目.一只跳蚤将被请上一个高空钢丝的正中央.钢丝很长,可以 ...

  4. LeetCode Degree of an Array

    原题链接在这里:https://leetcode.com/problems/degree-of-an-array/description/ 题目: Given a non-empty array of ...

  5. 关于verilog中小数直接赋值

    verilog中小数直接赋值的话小数会近似成1,如0.1,0.6,0.9赋值的话就会变成1,5.1,5.9也都会变成6.并且quartus默认小数是64位.

  6. Oracle与Mysql操作表序列

    一.Oracle添加表序列 CREATE SEQUENCE name -- 序列名 INCREMENT BY -- 每次加几个 START WITH -- 从几开始计数 MINVALUE --- 最小 ...

  7. mybatis参数

    分是不是用@Param注解的这两种情况, 1,使用@Param注解,就不用理parameterType xml文档直接用 字符串的值,若是一个类,class.property 2, 不使用@Param ...

  8. STM8S103 PB4和PB5

    STM8S103的PB4和PB5只能配置成开漏输出,用作I2C通讯: PB4和PB5不能配置为推挽输出,来控制LED之类的,因为内部没有上拉电阻,IO拉高电压只有1.8V左右,要想控制LED,只能通过 ...

  9. Linux yum操作时出现Error: xz compression not available

    yum升级PHP版本的时候出现这个问题 由于CentOS6的系统安装了epel-release-latest-7.noarch.rpm 导致在使用yum命令时出现Error: xz compressi ...

  10. php redis 命令合集

    1.https://www.cnblogs.com/aipiaoborensheng/p/5666005.html 2.https://www.cnblogs.com/doanddo/p/734908 ...