函数对象基本介绍
函数对象是一个普通的类对象,它的不普通之处在于它表现出一个函数的特征,即可以像普通函数那样进行函数调用操作。它的实现方式是对call操作符进行重载。
函数对象通常具有比普通函数更大的灵活性,例如,我们看下面这个例子。
bool GT6(int data) {
return data > 6;
}
这个函数的实现目的是比较一个数是否大于 6 ,加如我们现在需要这样的用户需求,就是测试一组 10 个用户输入的数字是否分别大于 1~10 ,如果使用普通的函数,我们就不得不实现 10 个这样的函数版本(暂时不考率函数模板)。换个思路,如果我们使用函数对象,结果就大不一样了:
class GT_int {
public:
GT_int(int pn):n(pn) {
}
bool operator()(int data) {
return data > n;
}
private:
int n;
}
以上是一个函数对象的实现方式,它重载了 call 运算符,使它可以实现和函数同样的操作,使用构造函数来确定所比较的值。以下是它的使用方式:
int data
for (int i = 1; cin >> data && i <= 10; ++i) {
GT_int gt(i);
cout<< gt(data)<< endl;
}
STL中的函数对象
函数对象在许多情况下非常有用,例如,它可以显示的指定STL算法库中的算法应该使用什么样的基本逻辑,例如,看下面的排序例子:
sort(svec.begin(), svec.end(), greater<string>());
一般说来,默认的sort函数使用’<’操作符对容器元素进行排序,而上面这个重载版本则使用了第三个参数greater<string>(),它是function头文件中定义的函数对象,可以比较第一个操作数是否大于第二个操作数,也就是说传递这个函数对象将使sort函数按降序方式进行排序。
在STL中,函数对象按照参数以及返回值的不同基本可以按照如下分类:
1.
产生器
没有参数
2.
一元函数
一个参数
3.
二元函数
两个参数
4.
一元谓词
一个参数,返回类型为bool型
5.
二元谓词
两个参数,返回类型为bool型
我们可以看一下函数accumulate的声明:
template <class InputIterator, class T, class BinaryOperation>
T accumulate ( InputIterator first, InputIterator last, T init,
BinaryOperation binary_op );
最后一个参数就要求是必须是一个二元函数。
函数适配器
前面已经介绍过容器适配器,迭代器适配器,今天我们来看一下函数适配器。和所有的适配器类似,函数适配器是用来让一个函数对象表现出另外一种类型的函数对象的特征。因为,许多情况下,我们所持有的函数对象或普通函数的参数个数或是返回值类型并不是我们想要的,这时候就需要函数适配器来为我们的函数进行适配。函数适配器最主要的可以分为两类:
1. 绑定适配器
该类适配器用于将二元函数适配成一元函数。
![](https://img-my.csdn.net/uploads/201207/19/1342684448_8543.png)
上图描述了绑定适配器的两种形式,binder1st将适配参数绑定到第一个参数上,binder2nd将适配参数绑定到第二个参数上。至于bind1st和bind2nd则实际上是为了避免冗长的声明而方便使用的适配模板函数而已。
我们还是先看一个例子:
count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10));
less_equal是STL为我们提供的一个函数对象,它有两个参数,器作用是比较第一个参数值是否<=第二个参数值。但是count_if要求我们第三个参数必须是一个一元谓词,所以我们用bind2nd对该函数对象进行适配,将10绑定到该函数对象的第二个参数上。现在cont_if执行的功能实际上就变成了查找给定序列中值<=10的元素的个数。
2. 反相器适配器
这一类适配器主要实现的功能是对函数的返回值进行适配,它对bool值进行取反,将true适配成false,false适配成ture。
例如我们如果想计算给定序列中元素值大于10的元素的个数,可以这样写:
count_if(vec.begin(), vec.end(), not1(bind2nd(less_equal<int>(), 10)));
STL为我们提供了两种反相器适配器,not1和not2,可进行适配的函数的参数分别是1个和两个。