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

一、函数对象

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


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


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


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


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


二、自定义函数对象

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include 
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 的定义:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
// TEMPLATE CLASS map
template <  class _Kty,
          class _Ty,
          class _Pr = less<_Kty>,
          class _Alloc = allocatorconst _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,再者,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 类的


模板实现:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
// 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 > mapTest; 则插入时按key 值从大到小排序(less,


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

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include 
#include 
#include 

using  namespace std;

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

int main( void)
{
    map <  int, string,  /*greater */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 */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一些算法中可以传入函数指针,实现自定义比较逻辑或者计算,同样地这些函数也可以使用函数对象来代替,直接看例程再稍

作分析:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include 
#include 
#include 
#include 

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[] = { 12345};
    vector< int> v(a, a +  5);

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

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

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

    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++之STL(八):函数对象、 函数对象与容器、函数对象与算法_第1张图片从零开始学C++之STL(八):函数对象、 函数对象与容器、函数对象与算法_第2张图片


参考:

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



你可能感兴趣的:(从零开始学C++,从零开始学C++)