从零开始学C++之STL(八):函数对象、 函数对象与容器、函数对象与算法

一、函数对象

1、函数对象(function object)也称为仿函数(functor)

2、一个行为类似函数的对象,它可以没有参数,也可以带有若干参数。

3、任何重载了调用运算符operator()的类的对象都满足函数对象的特征

4、函数对象可以把它称之为smart function。

5、STL中也定义了一些标准的函数对象,如果以功能划分,可以分为算术运算、关系运算、逻辑运算三大类。为了调用这些标准函数对象,需要包含头文件<functional>。

二、自定义函数对象

#include <iostream>
using namespace std;
class CFunObj
{
public:
    void operator()()
    {
        cout << "hello,function object!" << endl;
    }
};
int main()
{
    CFunObj fo;
    fo();
    CFunObj()();
    return 0;
}

注意:CFunObj()(); 表示先构造一个匿名对象,再调用operator();

三、函数对象与容器

在这边举map 容器的例子,大家都知道map 在插入元素的时候会自动排序,默认是根据key 从小到大排序,看map 的定义:

// TEMPLATE CLASS map
template < class _Kty,
         class _Ty,
         class _Pr = less<_Kty>,
         class _Alloc = allocator<pair<const _Kty, _Ty> > >
class map
    : public _Tree<_Tmap_traits<_Kty, _Ty, _Pr, _Alloc, false> >
{
    // ordered red-black tree of {key, mapped} values, unique keys
};

假设现在我们这样使用 map< int, string > mapTest; 那么默认的第三个参数 _Pr = less<int>,再者,map 继承的其中一个类

 _Tmap_traits 中有个成员:

 _Pr  comp;// the comparator predicate for keys 

跟踪进insert 函数,其中有这样一句:

if (_DEBUG_LT_PRED(this->comp, _Key(_Where._Mynode()), this->_Kfn(_Val)))

已知 #define _DEBUG_LT_PRED(pred, x, y) pred(x, y) 很明显地,comp 在这里当作函数对象使用,传入两个参数,回头看less 类的

模板实现:

// TEMPLATE STRUCT less
template<class _Ty>
struct less
        : public binary_function<_Ty, _Ty, bool>
{
    // functor for operator<
    bool operator()(const _Ty &_Left, const _Ty &_Right) const
    {
        // apply operator< to operands
        return (_Left < _Right);
    }
};

即实现了operator() 函数,左操作数小于右操作数时返回为真。

我们也可以在定义的时候传递第三个参数,如map< int, string, greater<int> > mapTest; 则插入时按key 值从大到小排序(less,

 greater 都是STL内置的类,里面实现了operator() 函数),甚至也可以自己实现一个类传递进去,如下面例程所示:

#include <map>
#include <string>
#include <iostream>

using namespace std;

struct MyGreater
{
    bool operator()(int left, int right)
    {
        return left > right;
    }
};

int main(void)
{
    map < int, string, /*greater<int> */MyGreater > mapTest;
    mapTest.insert(map<int, string>::value_type(1, "aaaa"));
    mapTest.insert(map<int, string>::value_type(3, "cccc"));
    mapTest.insert(map<int, string>::value_type(2, "bbbb"));


    for (map < int, string, /*greater<int> */MyGreater >::const_iterator it = mapTest.begin(); it != mapTest.end(); ++it)
    {
        cout << it->first << " " << it->second << endl;
    }

    return 0;
}

输出为:

3 cccc

2 bbbb

1 aaaa

MyGreater 类并不是以模板实现,只是比较key 值为int 类型的大小。

四、函数对象与算法

在STL一些算法中可以传入函数指针,实现自定义比较逻辑或者计算,同样地这些函数也可以使用函数对象来代替,直接看例程再稍

作分析:

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

void PrintFun(int n)
{
    cout << n << ' ';
}

void Add3(int &n)
{
    n += 3;
}

class PrintObj
{
public:
    void operator()(int n)
    {
        cout << n << ' ';
    }
};

class AddObj
{
public:
    AddObj(int number) : number_(number)
    {

    }
    void operator()(int &n)
    {
        n += number_;
    }

private:
    int number_;
};

class GreaterObj
{
public:
    GreaterObj(int number) : number_(number)
    {

    }
    bool operator()(int n)
    {
        return n > number_;
    }
private:
    int number_;
};


int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    vector<int> v(a, a + 5);

    /*for_each(v.begin(), v.end(), PrintFun);
    cout<<endl;*/

    for_each(v.begin(), v.end(), PrintObj());
    cout << endl;

    /*for_each(v.begin(), v.end(), Add3);
    for_each(v.begin(), v.end(), PrintFun);
    cout<<endl;*/

    for_each(v.begin(), v.end(), AddObj(5));
    for_each(v.begin(), v.end(), PrintFun);
    cout << endl;


    cout << count_if(a, a + 5, GreaterObj(3)) << endl; //计算大于3的元素个数

    return 0;
}

输出为:

1 2 3 4 5

6 7 8 9 10

2

回顾for_each 的源码,其中有这样一句: _Func(*_ChkFirst); 也就是将遍历得到的元素当作参数传入函数。

上面程序使用了函数对象,实际上可以这样理解 PrintObj()(*_ChkFirst); 即 PrintObj() 是一个匿名的函数对象,传入参

数,调用了operator() 函数进行打印输出。使用函数对象的好处是比较灵活,比如直接使用函数Add3,那么只能将元素加3,而

使用函数对象Addobj(x), 想让元素加上多少就传递给Addobj类,构造一个对象即可,因为它可以保存一种状态(类成员)。

count_if 中的 GreaterObj(3) 就类似了,将遍历的元素当作参数传递给operator(), 即若元素比3大则返回为真。

五、STL内置的函数对象类

参考:

C++ primer 第四版 Effective C++ 3rd C++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏闻道于事

Java之分支和循环

Java中的分支语句: if语句: if语句的四种写法:   (1)   if(表达式_布尔值) {   ...   }   (2)   if(表达式_布...

3149
来自专栏PHP在线

PHP面试题,PHP笔试题

题目一: <?php echo -10%3; ?> 答案:-1。 考查:优先级。 因为-的优先级比%求余的优先级低, 也就是-(10%3)。 题目二: prin...

1K15
来自专栏Golang语言社区

Go 语言数据类型

在 Go 编程语言中,数据类型用于声明函数和变量。 数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充...

3227
来自专栏从流域到海域

《笨办法学Python》 第4课手记

《笨办法学Python》 第4课手记 这节课目的是让你掌握变量,跟C语言非常类似,很简单。 左边是变量名用”=”号给变量赋值。 不同的是我没有看到变量声明,作者...

1898
来自专栏柠檬先生

你不知道的javaScript笔记(4)

类型: JavaScript 有7种内置类型 空值 (null) 未定义(undefined) 布尔值(boolean) 数字(number) 字符串(stri...

2255
来自专栏智能算法

Python学习(四)---- 列表生成式、生成器、迭代器和内置函数

https://blog.csdn.net/fgf00/article/details/52061971

972
来自专栏noteless

[三]基础数据类型之Integer详解

2263
来自专栏眯眯眼猫头鹰的小树杈

leetcode 343. Integer Break

这里应用了一个数学的思路。假设我们有一个数字n,该数组可以随机分解为t和n-t。当分解为n/2时可以获得最大的乘积。因此t取n/2时可以得到最好的结果。但是这里...

963
来自专栏武培轩的专栏

排序算法-插入排序

算法简介 插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应...

2764
来自专栏老司机的技术博客

宝宝都能学会的python编程教程6:列表(list)

上期编程题的答案如上图。 列表(list) list是一种有序的集合,可以随时添加和删除其中的元素。 当索引超出了范围时,Python会报一个IndexErr...

3676

扫码关注云+社区

领取腾讯云代金券