我们在前一节介绍了函数对象的概念,本节介绍基于函数对象的一个概念–函数对象适配器。
在讲函数对象适配器之前,我们来讲一讲普通函数,函数对象和函数对象适配器的关系。
普通函数是对程序功能的一种封装。这种封装只提供了方法,却没有提供属性。所以当我们希望使用某些属性时,我们往往使用普通函数的升级版–函数对象。我们可以将常用算法和函数对象结合起来使用,这样能完成更复杂的功能。当我们定义了一个函数对象,而这个函数对象又跟我们的需求有一定的偏差时,我们就需要函数对象适配器了。函数对象适配器本质上任然是一个函数;函数对象适配器提供了对函数对象或者普通函数的操作,使其能够根据我们的需求来修改函数对象或者普通函数的功能。下面我们从一个案例开始讲起。
创建一个vector和一个函数对象print,打印vector中的所有元素。
#include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; class print_int{ public: void operator()(int i){ cout << i << " "; } }; int main(){ vector <int> v; for (int i = 0; i < 10; i++){ v.push_back(i + 1); } for_each(v.begin(), v.end(), print_int()); cout << endl; return 0; }
我们现在又有了一个新的需求,打印vector中所有大于5的元素。
我们对以上代码进行微调,就可以完成该功能。#include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; class print_int{ public: void operator()(int i){ cout << i << " "; } }; class print_int_5{ public: void operator()(int i){ if (i > 5){ cout << i << " "; } } }; int main(){ vector <int> v; for (int i = 0; i < 10; i++){ v.push_back(i + 1); } for_each(v.begin(), v.end(), print_int_5()); cout << endl; return 0; }
我们现在又有需求了,打印vector中所有大于2的元素。如果我们不停的更改需求,我们就要不停的更改源代码,这是我们不愿意看到的。
那么我们有没有什么好的方法去应对这些需求呢?
答案就是:函数对象适配器
我们现在已经有个打印元素的一元函数对象print_int,我们使用函数对象适配器修改该函数对象,便能完成打印指定指定元素的功能。我们如何使用一个函数对象适配器呢?
- 首先让自定义的函数对象public继承一个父类。这里有两个选择:binary_function 和 unary_function。如果有两个参数选择前者。
- 定义一个函数对象作为参数传入函数对象适配器。常见的函数对象适配器有:
- 绑定适配器 bind1st bind2nd (bind1st绑定第一个参数, bind2nd绑定第二个参数)
- 取反适配器 not1 not2 (not1作用于一元函数对象,not2作用于二元函数对象)
- 普通函数适配器 ptr_fun
- 作用于类中方法的适配器 mem_fun mem_fun_ref
加const
//打印小于指定数字的vector容器中的元素 #include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; class print_int:public binary_function<int,int,void>{ public: void operator()(int i,int num)const{ if (i < num){ cout << i << " "; } } }; int main(){ vector <int> v; for (int i = 0; i < 10; i++){ v.push_back(i + 1); } for_each(v.begin(), v.end(), bind2nd(print_int(),5)); //绑定第二个参数5 cout << endl; return 0; }
取反适配器的例子:
#include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; class compare_num:public binary_function<int,int,bool>{ public: bool operator()(int i, int num) const { return i > num; } }; class compare_5:public unary_function<int,bool>{ public: bool operator()(int i) const { return i > 5; } }; int main(){ vector <int> v; for (int i = 0; i < 10; i++){ v.push_back(i + 1); } cout << endl; vector<int>::iterator i = find_if(v.begin(), v.end(), bind2nd(compare_num(),6)); if (i == v.end()){ cout << "cannot find the number!" << endl; } else{ cout << "find num: " << *i << endl; } auto j = find_if(v.begin(), v.end(), not1(compare_5())); //取反适配器的用法 if (j == v.end()){ cout << "cannot find the number!" << endl; } else{ cout << "find num: " << *j << endl; } return 0; }
普通函数适配器——ptr_fun
#include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; void print(int i,int j){ if (i > j){ cout << i << " "; } } void test02(){ vector<int> v; for (int i = 0; i < 10; i++){ v.push_back(i); } for_each(v.begin(), v.end(), bind2nd(ptr_fun(print),5)); //使用ptr_fun将普通函数转换为函数对象,然后给函数对象绑定参数。 cout << endl; } int main(){ test02(); return 0; }
最后,我们介绍mem_fun 和 mem_fun_ref
我们看一下场景。我们在以上的事例中使用的都是vector<int>
数据类型实际上vector可以存放标准数据类型,对象和指针。当我们存放的是对象时,对象本身具有方法。这个方法是定义在类中的成员函数。那么如何将成员函数转换成函数对象呢?
这个时候就要使用mem_fun_ref
扩展:如果容器中存放的是指针,那么就是使用方法mem_fun
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
class Teacher{
public:
int id;
int age;
Teacher(int id, int age) :id(id), age(age){}
void print(){
cout << "id: " << this->id << " age: " << this->age << endl;
}
void operator()(Teacher &t){
cout << "id: " << t.id << " age: " << t.age << endl;
}
};
class Teacher_print{
public:
void operator()(Teacher &t){
cout << "id: " << t.id << " age: " << t.age << endl;
}
};
int main(){
vector<Teacher> v;
vector<Teacher *>v2;
Teacher t1(1, 2), t2(3, 4), t3(5, 6);
v.push_back(t1);
v.push_back(t2);
v.push_back(t3);
for_each(v.begin(), v.end(), Teacher_print()); //这里的匿名函数对象Teacher_print()可以替换成t1。因为我在class Teacher中重载了函数调用运算符。t1即是一个类定义的对象,同时也是一个函数对象
cout << endl;
for_each(v.begin(), v.end(),mem_fun_ref(&Teacher::print));
cout << endl;
v2.push_back(&t1);
v2.push_back(&t2);
v2.push_back(&t3);
for_each(v2.begin(), v2.end(), mem_fun(&Teacher::print));
cout << endl;
return 0;
}