提到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. 升级Tensorflow到1.2版本

    TensorFlow 今天发布最新版 1.2.0,公布了14大最新功能.新智元带来最新介绍,包括 API 的重要变化.contrib API的变化和Bug 修复及其他改变.附代码链接. 主要的功能和改 ...

  2. linux 段错误常见处理方法

    1.如果没有产生core文件,可以查询系统log 通过 sudo cat /var/log/messages |grep segfault 或者 sudo dmesg|grep segfault 获得 ...

  3. (效果一)js实现上拉加载

    实现思路:获取滚动元素的高度,滚动条距离顶部的距离,滚动条的高度, 算式:可视窗口的高度 + 滚动条距离顶部的距离 == 滚动条的高度就说明到底部. HTML <!doctype html> ...

  4. Ant入门之引用外部jar文件

    笔者在java项目开发中经常遇到引用外部Jar包的情况,使用ant打包过程中需要对其引用.现在此简单记忆以飨来者. 此处引用Log4j,具体程序HelloLog4j.java: package oat ...

  5. iOS UI调试工具 -- UIDebuggingInformationOverlay

    英文原文: http://ryanipete.com/blog/ios/swift/objective-c/uidebugginginformationoverlay/ 无意中看到iOS自带调试工具 ...

  6. serf  简单使用

    1. 介绍 // 以下为官方介绍,说白了就是进行系统的集群节点管理 Serf uses an efficient gossip protocol to solve three major proble ...

  7. window下TortoiseGit的安装和使用

    一.安装git for windows 首先下载git for windows客户端https://git-for-windows.github.io/安装过程没什么特别的,下载完安装包之后,按照提示 ...

  8. CENTOS7配置静态IP后无法ping通外部网络的问题

    我今天想谈论的并不是如何配置静态IP,这样的话题已经有好多高手再谈. 我想谈的是为什么,我按照他们的教程无论如何也要发生各种问题,没办法连接外网的问题. 先给大家看我的最终版配置方案:我只修改了一个文 ...

  9. Java知识点汇总

    Java中泛型的本质 Java中静态变量的适用场景 Java类加载原理及类加载器 Java中对Clone的理解 Java中HashMap的实现 Java中Collection和Collections的 ...

  10. excel linux扩展

    接近我的示例 http://ju.outofmemory.cn/entry/116399 http://tanxw.blog.51cto.com/4309543/1618576 http://blog ...