博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++_标准模板库STL概念介绍3-函数对象
阅读量:5750 次
发布时间:2019-06-18

本文共 5052 字,大约阅读时间需要 16 分钟。

函数对象也叫做函数符(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值的二元函数是二元谓词

 

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

1 //functor.cpp  -- using a functor 2  3 #include 
4 #include
5 #include
6 #include
7 8 template
9 class TooBig10 {11 12 };13 14 void outint(int n) {std::cout<< n << " ";}15 16 int main()17 {18 using std::list;19 using std::cout;20 using std::endl;21 22 TooBig
f100(100);23 int vals[10] = { 50, 100, 90, 180, 60, 210, 415, 88, 188, 201};24 list
yadayada = {vals, vals+10};25 list
etcetera = {vals, vals+10}; 26 27 cout<<"Original lists:\n";28 for_each(yadayada.begin(), yadayada.end(), outint);29 cout<
(200));34 cout<<"Trimmed lists:\n";35 for_each(yadayada.begin(), yadayada.end(), outint);36 cout<

 

再来一个例子:

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

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));

 

 

1 #include 
2 #include
3 #include
4 #include
5 #include
6 7 void Show(double); 8 const int LIM = 6; 9 10 11 int main()12 {13 using namespace std;14 double arr1[LIM] = {
28, 29, 30, 35, 38, 59};15 double arr2[LIM] = {
63, 65, 69, 75, 80, 99};16 vector
gr8(arr1, arr1+LIM);17 vector
m8(arr2, arr2+LIM);18 cout.setf(ios_base::fixed);19 cout.precision(1);20 cout<<"gr8:\t";21 for_each(gr8.begin(), gr8.end(), Show);22 cout<
sum(LIM);28 transform(gr8.begin(), gr8.end(), m8.begin(), sum.begin(), plus
());29 cout<<"sum: \t";30 for_each(sum.begin(), sum.end(), Show);31 cout<
prod(LIM);34 transform(gr8.begin(), gr8.end(), m8.begin(), prod.begin(), bind1st(multiplies
(), 2.5));35 cout<<"prod: \t";36 for_each(prod.begin(), prod.end(), Show);37 cout<

转载于:https://www.cnblogs.com/grooovvve/p/10467800.html

你可能感兴趣的文章
看linux书籍做的一些重要笔记(2011.07.03更新)
查看>>
Exchange server 2010系列教程之一 安装Exchange 2010准备条件
查看>>
POI 生成 xls 文件使用总结(快速入门)
查看>>
CString、Char* ,char [20]、wchar_t、unsigned short转化
查看>>
从案例学RxAndroid开发(上)
查看>>
debian 下安装megacli
查看>>
我写的第一个shell脚本(2009-06-08)
查看>>
ubutun 中 Eclipse中 快捷键 Alt + / 不能使用的问题
查看>>
Redis学习手册(内存优化)
查看>>
浅尝TensorFlow on Kubernetes
查看>>
wnmp-3.1.0安装cakephp启动失败处理
查看>>
springboot系列十 Spring-Data-Redis
查看>>
Confluence 6 注册外部小工具
查看>>
excel进行矩阵计算
查看>>
基于Android平台的动态生成控件和动态改变控件位置的方法
查看>>
Java集合(二) Map 架构
查看>>
linux 死机分析
查看>>
BOM
查看>>
LeetCode:Nim Game - 尼姆博弈
查看>>
Alpha冲刺&总结报告(12/12)(麻瓜制造者)
查看>>