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

什么是函数对象

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

 
class Print
{
public:
void operator()(int n)
{
std::cout<<n<<std::endl;
return ;
}
};
int
main(int argc, char **argv)
{
Print print;
print(372);
print.operator()(372); //~ 显式调用
return 0;
}

  其实我们早就开始使用函数对象了,当你写下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>的一个典型实现可能是这样的:

   
template <class T>
class less
{
public:
bool operator()(const T&l, const T&r)const
{
return l < r;
}
};

函数对象的分类

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

 
template<class Arg, class Res>
struct unary_function //~ 一元函数对象基类
{
typedef Arg argument_type;
typedef Res result_type;
};
 
template<class Arg1, class Arg2, class Res>
struct binary_function //~ 二元函数对象基类
{
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Res result_type;
};

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

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>()的第一个或者第二个参数:

 
int
main(int argc, char **argv)
{
vector<int> v;
for(int i = 0; i < 1000; ++i)
{
v.push_back(i);
}
vector<int>::iterator it;
it = find_if(v.begin(), v.end(), bind1st(equal_to<int>(), 372));
std::cout<<*it<<std::endl;
return 0;
}

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

 
// bind1st
template<class Op>
class binder1st : public unary_function
<typename Op::second_argument_type,
typename Op::result_type>
{
Op op_;
typename Op::first_argument_type first_arg_;
 
public:
binder1st(const Op& op,
const typename Op::first_argument_type&
first_arg) : op_(op),
first_arg_(first_arg) {}
 
typename Op::result_type operator()
(const typename Op::second_argument_type& arg) const
{
return op_(first_arg_, arg);
}
};
 
template<class Op, class Arg>
inline binder1st<Op> bind1st(const Op& op,
const Arg& arg)
{
return binder1st<Op>(op, arg);
}
 
// bind2nd
template<class Op>
class binder2nd : public unary_function
<typename Op::first_argument_type,
typename Op::result_type>
{
Op op_;
typename Op::second_argument_type second_arg_;
 
public:
binder2nd(const Op& op,
const typename Op::second_argument_type&
second_arg) : op_(op),
second_arg_(second_arg) {}
 
typename Op::result_type operator()(const typename
Op::argument_type& arg) const
{
return op_(arg, second_arg_);
}
};
 
template<class Op, class Arg>
inline binder2nd<Op> bind2nd(const Op& op,
const Arg& arg)
{
return binder2nd<Op>(op, arg);
}
Negaters

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

Member Function Adapters

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

 
int
main(int argc, char **argv)
{
vector<list<int> > v;
v.push_back(list<int>());
vector<list<int> >::iterator it;
for(it = v.begin(); it != v.end(); ++it)
{
for(int i = 0; i < 20; ++i)
{
(*it).insert((*it).begin(), i);
}
}
for_each(v.begin(), v.end(), std::mem_fun_ref(&list<int>::sort));
for(it = v.begin(); it != v.end(); ++it)
{
for(list<int>::iterator lt; lt != (*it).end(); ++lt)
{
std::cout<<*lt<<std::endl;
}
}
return 0;
}

上面的例子中,遍历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. HSRP/VRRP/GLBP

    当网络足够大的时候,网络规划师要考虑的技光是网络本身的性能问题,冗余技术也是必不可少的. 常见的冗余网关技术有• 热备份路由协议(HSRP).• 虚拟路由器冗余协议(VRRP)• 网关负载均衡协议(G ...

  2. uid

    var uid = 0 function nextUid() { return ++uid }

  3. ArcGIS Runtime SDK for WPF之测量距离和面积

    bu不多说,上代码 using System.Windows; using ESRI.ArcGIS.Client; using ESRI.ArcGIS.Client.Tasks; using ESRI ...

  4. CentOS7上部署https

    目前很多浏览器都加强了html都安全性,要求配置https. 下面都例子是在CentOS7上的Apache配置https都过程. 一.生成证书 用OpenSSL生成key和证书: mkdir /etc ...

  5. (转)c#实现开机自启动

    RegistryKey key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersio ...

  6. linux 定时脚本任务的创建

    参考资料https://my.oschina.net/xsh1208/blog/512810 定时脚本任务创建过程 1. 启动/终止 crontab 服务 一般使用这个命令/sbin/service ...

  7. ajax级联实现

    效果如下: 选择第一项,第二项.第三项的内容跟着改变. 选择第二项,第三项的内容跟着改变. 第三项则不影响第一项和第二项. 有几点值得提: 1.html到底是前台拼接还是后台拼接. 我选择的是前台拼接 ...

  8. Oracle 2套rac集群指向单机多实例的复制搭建

    Oracle 2套rac集群指向单机多实例的复制搭建 由于环境限制,现在需要把2套rac集群通过dg复制指向远端的单机多实例上面. rac指向第一个实例的前面已经有文档 这里直接添加第二个实例的复制搭 ...

  9. Sql server 2008 R2 正在关闭[0x80041033]

    1. 事件起因, 昨天还访问的好好的, 然后系统一更新, 今天访问的时候, 就报什么 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器.请验证实例名称是 ...

  10. 第四章 Javac编译原理

    4.1 Javac是什么 是一种编译器,将JAVA源代码(.java文件)语言先转化成JVM能够识别的一种语言(.class文件),然后由JVM将JVM语言再转化成当前机器可以识别的机器语言. 4.2 ...