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:
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:
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:
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;
};
自定义仿函数只要继承上面的一个就可以与配接器搭配使用。