函数对象也叫做函数符(functor)

函数符是可以以函数方式和( )结合使用的任意对象。

包括函数名指向函数的指针重载了()运算符的类对象

可以这样定义一个类:

class Linear

{

private:

  double slope;

  double y0;

public:

  Linear(double s1_=1, double y_ = 0):slope(s1_),y0(y_)  { }

  double operator() (double x) {return y0 + slope * x;}

}

重载了()运算符后,就可像使用函数那样使用Linear对象;

Linear f1;

Linear f2(2.5, 10.0);

double y1 = f1(12.5);   // right-hand side is f1.operator( )(12.5)

double y2 = f2(0.4);

接下来讨论另一个例子:

for_each(books.begin(), books.end(), ShowReview);

通常,第3个参数可以是常规函数,也可以是函数符。

现在有个问题,如何声明第3个参数?

不能把它声明为函数指针,因为函数指针指定了参数类型。

由于容器是可以包含任意类型的,所以预先也无法知道应使用哪种类型。

STL是通过模板解决该问题。

for_each的原型看上去就像这样:

template <class InputIterator, class Function>

Function for_each(InputIterator first, InputIterator last, Function f);

ShowReview的原型如下:

void ShowReview(const Review &);

这样标识符ShowReview的类型将为void(*)(const Review &)。这也是赋给模板参数Function的类型。

Function还可以表示具有重载()运算符的类类型。

最终,for_each()代码将具有一个使用f()的表达式。

在ShowReview示例中,f是指向函数的指针,而f()调用函数。

如果最后的for_each()参数是一个对象,则f()将是调用其重载()运算符的对象。

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

一、函数符的概念

生成器是不用参数就可以调用的函数符;

一元函数是用一个参数就可以调用的函数符;

二元函数是用两个参数就可以调用的函数符;

返回bool值的一元函数是谓词

返回bool值的二元函数是二元谓词

接下来有一个例子演示类函数符适用的地方。

 //functor.cpp  -- using a functor

 #include <iostream>
#include <list>
#include <iterator>
#include <algorithm> template<class T>
class TooBig
{ }; void outint(int n) {std::cout<< n << " ";} int main()
{
using std::list;
using std::cout;
using std::endl; TooBig<int> f100();
int vals[] = {, , , , , , , , , };
list<int> yadayada = {vals, vals+};
list<int> etcetera = {vals, vals+}; cout<<"Original lists:\n";
for_each(yadayada.begin(), yadayada.end(), outint);
cout<<endl;
for_each(etcetera.begin(), etcetera.end(), outint);
cout<<endl;
yadayada.remove_if(f100);
etcetera.remove_if(TooBig<int>());
cout<<"Trimmed lists:\n";
for_each(yadayada.begin(), yadayada.end(), outint);
cout<<endl;
for_each(etcetera.begin(), etcetera.end(), outint);
cout<<endl;
return ;
}

再来一个例子:

假设已经有了一个接受两个参数的模板函数:

template <class T>

bool tooBig(const T & val, const T & lim)

{

return val >lim;

}

则可以使用将它转换为单个参数的函数对象:

template<class T>

class TooBig2

{

private:

T cutoff;

public:

TooBig2(const T & t):cutoff(t) { }

bool operator() (const T & v) {return tooBig<T>(v, cutoff);}

};

即可以这样做:

TooBig2<int> tB100(100);

int x;

cin>>x;

if(tB100(x))

...

类函数符TooBig2是一个函数适配器,使函数能够满足不同的接口。

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

二、预定义的函数符

STL定义了多个基本函数符,它们执行诸如将两个值相加、比较两个值是否相等操作。

提供这些函数对象是为了支持将函数作为参数的STL函数。

例如,考虑函数transform(),它有两个版本。

第一个版本使用接受4个参数的函数:

  前两个参数是指定容器区间的迭代器;

  第3个参数是指定将结果复制到哪里的迭代器;

  最后一个参数是一个函数符;

  const int LIM =5;

  double arr1[LIM] = {36, 39, 42, 45, 48};

  vector<double> gr8(arr1, arr1+LIM);

  ostream_iterator<double, char> out(cout, " ");

  transform(gr8.begin(), gr8.end(), out, sqrt);

第二个版本使用接受5个参数的函数:

  第3个参数标识第二个区间的起始位置;

  如果m8是另一个vector<double>对象,mean(double, double)返回两个值的平均值,

  则下面的代码将输出来自gr8和m8的值的平均值:

  transform(gr8.begin(), gr8.end(), m8.begin(), out, mean);

现在假设要将两个数组相加,不能将+作为参数,因为对于类型double来说,+是内置的运算符,而不是函数。

可以定义一个将两个数相加的函数,然后使用它:

double add(double x, double y)  {return x+y;}

...

transform(gr8.begin(), gr8.end(), m8.begin(), out, add);

但是这样做的话,就必须为每种类型单独定义一个add函数。更好的办法是定义一个模板(除非STL已经有一个模板了);

头文件functional定义了多个模板类函数对象,其中包括plus<>()。

因此可以用plus<>类完成常规的相加运算;

#include <functional>

...

plus<double> add;

double y =add(2.2, 3.4);

transform(gr8.begin(), gr8.end(), m8.begin(), out, plus<double>);  //它使得将函数对象作为参数非常方便。

对于所有内置的运算符,STL都提供了等价的函数符。

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

三、自适应函数符和函数适配器

自适应的英文名叫adapter,其实也可以理解成适配器。函数符适配器。

适配的目的是满足将原先不满足要求的函数符,适配成满足要求的。就像是插座转换器一样。

例如:

transform只能接受一元函数参数。

multiplies()函数符可以执行乘法运算符, 但是它是二元函数。

因此需要函数适配器,将接受2个参数的函数符转换为接受1个参数的函数符。

前面的示例TooBig2提供了这种方法。即用定义类函数符的方法。

那么是否能将这个过程自动化呢?是可以的。

就叫做自动化适配

STL使用binder1st和binder2nd类自动完成这一过程。

它们将自适应二元函数转换为自适应一元函数。

而且前提是被适配的函数符必须是自适应的。自动化适配

接下来看一个例子:

binder1st(f2, val) f1;

这样的话f1(x)等价于f2(val,x);

f2被适配,当然f2必须是一个自适应函数时,才能实现。

这样看上去还是有点麻烦,STL提供了函数bind1st(),以简化binder1st类的使用。

bind1st(multiples<double>(), 2.5);

将gr8中的每个元素与2.5相乘,并显示结果的代码如下:

transform(gr8.begin(), gr8.end(), out, bind1st(multiples<double>(), 2.5));

 #include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional> void Show(double);
const int LIM = ; int main()
{
using namespace std;
double arr1[LIM] = {, , , , , };
double arr2[LIM] = {, , , , , };
vector<double> gr8(arr1, arr1+LIM);
vector<double> m8(arr2, arr2+LIM);
cout.setf(ios_base::fixed);
cout.precision();
cout<<"gr8:\t";
for_each(gr8.begin(), gr8.end(), Show);
cout<<endl;
cout<<"m8: \t";
for_each(m8.begin(), m8.end(), Show);
cout<<endl; vector<double> sum(LIM);
transform(gr8.begin(), gr8.end(), m8.begin(), sum.begin(), plus<double>());
cout<<"sum: \t";
for_each(sum.begin(), sum.end(), Show);
cout<<endl; vector<double> prod(LIM);
transform(gr8.begin(), gr8.end(), m8.begin(), prod.begin(), bind1st(multiplies<double>(), 2.5));
cout<<"prod: \t";
for_each(prod.begin(), prod.end(), Show);
cout<<endl;
return ;
} void Show(double v)
{
std::cout.width();
std::cout<< v <<' ';
}

C++_标准模板库STL概念介绍3-函数对象的更多相关文章

  1. C++_标准模板库STL概念介绍5-其他库与总结

    C++还提供了其他一些类库,这些类库更加专用. 例如,头文件complex为复数提供了类模板complex,包含用于float.long和long double的具体化. 这个类提供了标准的复数运算以 ...

  2. C++_标准模板库STL概念介绍1-建立感性认知

    标准模板库的英文缩写是STL,即Standard Template Library. STL里面有什么呢? 它提供了一组表示容器.迭代器.函数对象和算法的模板. 容器是一个与数组类似的单元,可以存储若 ...

  3. C++_标准模板库STL概念介绍4-算法

    STL包含很多处理容器的非成员函数: sort() copy() find() random_shuffle() set_union() set_intersection() set_differen ...

  4. C++_标准模板库STL概念介绍2-泛型编程

    有了之前使用STL的经验后,接下来讨论泛型编程及其底层的理念: 首先我们知道STL只是泛型编程的一种: 而面向对象的编程方式关注的是编程的数据方面: 而泛型编程关注的是算法: 但是,他们之间的一个重要 ...

  5. STL学习系列之一——标准模板库STL介绍

    库是一系列程序组件的集合,他们可以在不同的程序中重复使用.C++语言按照传统的习惯,提供了由各种各样的函数组成的库,用于完成诸如输入/输出.数学计算等功能. 1. STL介绍 标准模板库STL是当今每 ...

  6. C++ 标准模板库STL 队列 queue 使用方法与应用介绍

    C++ 标准模板库STL 队列 queue 使用方法与应用介绍 queue queue模板类的定义在<queue>头文件中. 与stack模板类很相似,queue模板类也需要两个模板参数, ...

  7. 标准模板库--STL

    标准模板库STL 1.泛型程序设计 C++ 语言的核心优势之一就是便于软件的重用 C++中有两个方面体现重用: 1.面向对象的思想:继承和多态,标准类库 2.泛型程序设计(generic progra ...

  8. 实验8 标准模板库STL

    一.实验目的与要求: 了解标准模板库STL中的容器.迭代器.函数对象和算法等基本概念. 掌握STL,并能应用STL解决实际问题. 二.实验过程: 完成实验8标准模板库STL中练习题,见:http:// ...

  9. cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器

    cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器map(映射,key不能重复,一对一对的,value_type(1, "one")),mu ...

随机推荐

  1. 【HDU5391】Zball in Tina Town

    [题目大意] 一个球初始体积为1,一天天变大,第一天变大1倍,第二天变大2倍,第n天变大n倍.问当第 n-1天的时候,体积变为多少.注意答案对n取模. [题解] 根据威尔逊定理:(n-1)! mod ...

  2. array_splice()函数 ,删除数组中的某个值

    array_splice() 这个函数是真的皮,有好多种方法,但是最后还是在PHP官方的文档找到了合理的解释的用法 花了大概半个小时 $arr = array('a','b','c','d'); ar ...

  3. 293. Flip Game只翻转一步的加减号翻转游戏

    [抄题]: You are playing the following Flip Game with your friend: Given a string that contains only th ...

  4. c语言练习 二维数组 年平均降水量 月平均降水量

    #define YEARS 5#define MONTHES 12 int main(void) { const float rain[YEARS][MONTHES] = { {4.3,4.3,4.3 ...

  5. Part5核心初始化_lesson3---关闭看门狗

    1.看门狗---作用 2.看门狗工作方式 3.原理图 时钟源来自于PCLK经过分频器,经过选择器,输出到作为看门狗定时器,WTDAT为一个预载值,当它计数为零的时候,还没有给WTDAT赋值,那么它会发 ...

  6. 【原创】linux signal处理中的几个问题(suse下莫名其妙死锁的处理)

    我在CSDN专栏写过的,老帖子最近发现在腾讯的CVM上,服务器总是平凡的死锁后查明真像为 当你发生sig 11的异常时,会进入处理函数 signalHandler同时此时生成相应的dump file时 ...

  7. OpenCV2.3.0在VS中的配置

    本文为cherish总结,这里先收藏着了啦啦啦!!! vs中配置时尽量在全局配置中修改,否则每次新建项目都需要重新配置全局配置步骤如下:1.“视图”菜单 -> (其他窗口->)属性管理器 ...

  8. C# 将一个DataTable的结构直接复制到另一个DataTable

    DataTable.Clone();//仅复制表结构DataTable.Copy();//复制表结构及数据 DataTable.ImportRow(DataRow);//复制行数据到新表 DataRo ...

  9. HDU 4111 Alice and Bob (博弈+记忆化搜索)

    题意:给定 n 堆石头,然后有两种操作,一种是把从任意一堆拿走一个,另一种是把一个石子放到另一堆上. 析:整体看,这个题真是不好做,dp[a][b] 表示有 a 堆1个石子,b个操作,操作是指把其他的 ...

  10. delphi 指针 认识

    delphi 指针分为类型指针和无类型指针: 类型指针分为PChar.PInteger.PString等. 无类型指针Pointer. PPChar/PP...为指针的指针 @和Addr一样,为获取变 ...