STL学习总结之<仿函数>

1 仿函数的概念

仿函数,又名函数对象,是一个定义了operator ()的对象。仿函数的主要功能代码在仿函数类的operator ()体内完成。仿函数的妙处:

(1) 仿函数比一般函数更灵巧,可以用有状态,对于仿函数可以同时拥有两个状态的不同实体。

(2) 每个仿函数都有其型别,通过传递不同型别的仿函数当作template参数给容器,可以构造出型别不同的容器。

(3) 执行速度上,仿函数通常比函数指针快。

很多stl算法有一个函数参数,例如remove_if,for_each等,这个函数可以是普通的全局函数,仿函数,类的成员函数(非static,static可以作为全局函数使用),类的成员函数比较特殊,需要使用适配器函数mem_fun/mem_fun_ref包装才可以。 

2 仿函数的状态

仿函数可以拥有内部状态,但算法并不会改变随参数而来的仿函数的状态。仿函数是以passed by value(传值),不是passed by reference(传址)。这样的好处是可以传递常量和暂时表达式,缺点是无法存取仿函数的最终状态,因为算法内部改变的是仿函数的副本的状态。得到仿函数最终状态的两种方法:

(1) 以by reference的方式传递函数(eg1)。

(2) 运用for_each()算法的返回值(eg2)。如果for_each中的第三个参数是仿函数则for_each返回该仿函数的副本;如果for_each中的第三个参数是一个全局函数则返回一个函数指针。

eg1:

 

View Code
 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 class INIT
 6 {
 7 private:
 8     int value;
 9 public:
10     INIT(int m):value(m){}
11     int operator() ()
12     {
13         return value++;
14     }
15 };
16 class PRINT
17 {
18 public:
19     void operator() (int value)
20     {
21         cout<<value<<" ";
22     }
23 };
24 
25 int main()
26 {
27     vector<int>vec;
28     INIT init(1);
29     generate_n<back_insert_iterator<vector<int> >,int,INIT&>(back_inserter(vec),5,init);
30     for_each(vec.begin(),vec.end(),PRINT());
31     cout<<endl;
32     generate_n(back_inserter(vec),5,INIT(42));
33     for_each(vec.begin(),vec.end(),PRINT());
34     cout<<endl;
35     generate_n(back_inserter(vec),5,init);
36     for_each(vec.begin(),vec.end(),PRINT());
37     cout<<endl;
38     return 0;
39 }
40 输出:
41 1 2 3 4 5
42 1 2 3 4 5 42 43 44 45 46
43 1 2 3 4 5 42 43 44 45 46 6 7 8 9 10

 

eg2:

View Code
 1 #include<iostream>
 2 
 3 #include<vector>
 4 
 5 #include<algorithm>
 6 
 7 #include<iterator>
 8 
 9 using namespace std;
10 
11 class INIT
12 
13 {
14 
15 private:
16 
17          int value;
18 
19 public:
20 
21          INIT(int m):value(m){}
22 
23          int operator() ()
24 
25          {
26 
27                    return value++;
28 
29          }
30 
31 };
32 
33  
34 
35 class MeanValue
36 
37 {
38 
39 private:
40 
41          long num;
42 
43          long sum;
44 
45 public:
46 
47          MeanValue():num(0),sum(0){}
48 
49          void operator() (int value)
50 
51          {
52 
53                    num++;
54 
55                    sum+=value;
56 
57          }
58 
59          operator double()//类型转换函数
60 
61          {
62 
63                    return static_cast<double>(sum)/static_cast<double>(num);
64 
65          }
66 
67 };
68 
69  
70 
71 int main()
72 
73 {
74 
75          vector<int> coll;
76 
77          generate_n(back_inserter(coll),10,INIT(1));
78 
79          double mv = for_each(coll.begin(),coll.end(),MeanValue());//for_each返回仿函数类型
80 
81          //MeanValue mv = for_each(coll.begin(),coll.end(),MeanValue());//这样写也可以,输出时会                                                                                                                           自动将mv转换成double
82 
83          cout<<"mean value: "<<mv<<endl;
84 
85          return 0;
86 
87 }

 

3 判断式

所谓判断式就是一个返回布尔值的一个函数或仿函数。注:最好总是将判断式的operator()声明为const成员函数,应保证判断式不应该因为调用而改变自身状态,判断式的副本应该和其正本有着相同的状态。

4 预定义的仿函数

要使用这些函数必须包含<functional>

仿函数

效果

negate<type>()

- param

plus<type>()

param1 + param2

minus<type>()

param1 - param2

multiplies<type>()

param1 * param2

divides<type>()

param1 / param2

modulus<type>()

param1 % param2

equal_to<type>()

param1 == param2

not_equal_to<type>(0

param1 != param2

less<type>()

param1 < param2

greater<type>()

param1 > param2

less_equal<type>()

param1 <= param2

greater<type>()

param1 > param2

less_equal<type>()

param1 <= param2

greater_equal<type>()

param1 >= param2

logical_not<type>()

! param

logical_and<type>()

param1 && param2

logical_or<type>()

param1 || param2

5 函数配接器

所谓函数配接器是指能够将仿函数和另一个函数(或某个值,或某个一般函数)结合起来的仿函数。函数配接器也声明在<functional>。例如:

find_if(coll.begin(),coll.end(),bind2nd(greater<int>(),42));

其中的表达式bind2nd(greater<int>(),42)导致一个组合型仿函数,检查某个int值是否大于42,bind2nd可以将一个二元仿函数转换成一个一元仿函数,它通常将第二个参数传给由第一个参数指出的二元仿函数,作为后者的第二参数。

预定义的函数配接器:

表达式

效果

bind1st(op, value)

op(value, param)

bind2nd(op, value)

op(param, value)

not1(op)

!op(param)

not2(op)

!op(param1,param2)

 

5.1 针对成员函数而设计的函数配接器

在算法中使用类的成员函数。

C++标准程序库提供了一些额外的函数配接器,透过它们可以针对群集内的每个元素调用其成员函数。成员函数配接器:

表达式

效果

mem_fun_ref(op)

调用op,那是某对象的一个成员函数(const和非const)

mem_fun(op)

调用op,op是某对象指针的成员函数(const和非const)

 

不能直接把一个对象的成员函数传给一个算法,必须用配接器。算法对传入的指针调用的是operator(),而不是调用该指针所指的成员函数,配接器mem_fun_ref和mem_fun将operator()调用动作做了适当转换。

eg1:

View Code
 1 #include<iostream>
 2 
 3 #include<functional>
 4 
 5 #include<vector>
 6 
 7 #include<algorithm>
 8 
 9 using namespace std;
10 
11 class student
12 
13 {
14 
15 private:
16 
17          int num;
18 
19 public:
20 
21          student(int n):num(n){}
22 
23          void print()
24 
25          {
26 
27                    cout<<num<<" ";
28 
29          }
30 
31 };
32 
33  
34 
35 int main()
36 
37 {
38 
39          vector<student>vec;
40 
41          for(int i=0; i<10; ++i)
42 
43          {
44 
45                    vec.push_back(student(i));
46 
47          }
48 
49          for_each(vec.begin(),vec.end(),mem_fun_ref(&student::print));
50 
51          cout<<endl;
52 
53          return 0;
54 
55 }

 

注: &student::print为指向student成员函数print的成员函数指针,其类型为:

void (student::*) ()。

5.2 针对一般函数(非成员函数)设计的函数配接器

函数配接器ptr_fun()允许在其他函数配接器中使用一般函数

eg:

bool  check(int elem); //对元素进行检查的一般全局函数

pos = find_if(coll.begin(), coll.end(), not1(ptr_fun(check)));//这里不能直接使用not1(check),因为要想让not1直接使用check,需要check提供一些特性型别。
5.3 让自定义仿函数直接使用函数配接器

自定义的仿函数要与配接器搭配使用,必须满足某些条件,必须提供一些型别成员来反映其参数和返回值的型别,C++标准程序库提供了一些结构如下:

template<class Arg, class Result>

struct unary_function{

         typedef Arg argument_type;

         typedef Result result_type;

};

template <class Arg1, class Arg2, class Result>

struct binary_function{

         typedef Arg1 first_argument_type;

         typedef Arg2 second_argument_type;

         typedef Result result_type;

};

自定义仿函数只要继承上面的一个就可以与配接器搭配使用。

posted @ 2013-03-09 11:24  landy聪  阅读(585)  评论(0编辑  收藏  举报