學習資源匯總來源於于中國MOOC,北大面嚮程序對象設計
一般定义是:如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象。
先给一个例子:
#include<iostream>
using namespace std;
class average{
public:
int operator()(int a1,int a2,int a3){return (a1 + a2 + a3)/3; }
};
int main()
{
average a;
cout<< "a.operator()(2,2,2) = "<<a.operator()(2,2,2)<<endl;
average b;
cout<<"b(2,2,2) = "<<b(2,2,2);
return 0;
}
执行效果
代码分析:
average a;
是函数对象的定义;a.operator()(2,2,2)
第一个括号的是对括号运算符的调用,第二个括号内是参数的传递;b(2,2,2)
使所定义的对象能够调用()运算符;如此调用,类似于函数的普通调用,因此叫做函数对象。
与普通函数相比,函数对象比函数更加灵活,函数对象的优势:
- 函数对象可以有自己的状态。我们可以在类中定义状态变量,这样一个函数对象在多次的调用中可以共享这个状态;
- 函数对象有自己特有的类型。我们可以传递相应的类型作为参数来实例化相应的模板,比如说带参数的函数形参。
函数对象实例
STL中的模板
template<class InIt, class T, class Pred>
T accumulate(InIt first, InIt last, T val, Pred pr);
其中
pr 就是个函数对象,对[first,last)中的每个迭代器 I, 执行 val = pr(val,* I) ,返回最终的val。
Pr也可以是个函数,或者函数指针。
在numeric 头文件中 accumulate 的源代码如下:
template <class InIt, class T, class Pred>
T accumulate(InIt first, Init last, T init, Pred op)
{
for (; first != last; ++first)
init = op(init, *first);
return init;
};
其中pr只能是函数指针或者函数对象,而调用时的实参实参只能是函数名、函数指针或者函数对象。而T init是初始值,累加到该值上,然后返回,一般初始为0.
示例
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
template<class T>
class sum{
public:
T operator()(T &total,const T &value){
total += value;
return total;
}
};
int comp(int total,int value)
{
return total + value * 4;
}
int main()
{
const int SIZE = 10;
int a[] = { 1,2,3,4,5,6,7,8,9,10 };
vector<int> v(a,a + SIZE);
int _sum = accumulate(v.begin(),v.end(),0,sum<int>());
cout<<_sum<<endl;
_sum = accumulate(v.begin(),v.end(),0,comp);
cout<<_sum<<endl;
return 0;
}
执行效果
其中
int _sum = accumulate(v.begin(),v.end(),0,sum<int>());
sum()实参是函数对象,括号是必须有的,说明是调用函数对象。
实例化
int accumulate(accumulate(vector<int>::iterator first,
vector<int>::iterator last, int init,sum<int> pr)
{
for ( ; first != last; ++first)
init = op(init, *first);
return init;
}
实例化之后最后一个参数是函数对象。而为什么不带括号呢?这是因为构造函数只会在最开始调用的时候进行一次赋值。
_sum = accumulate(v.begin(),v.end(),0,comp);
comp即调用普通的函数,不需要括号。
实例化为
int accumulate(accumulate(vector<int>::iterator first,
vector<int>::iterator last, int init,int (*pr)(int a,int b)){
for ( ; first != last; ++first)
init = pr(init, *first);
return init;
}
实例化之后最后一个参数是函数指针加参数列表,因为函数名字的类型是函数指针,因此本行将 accumulate 模板实例化后得到的模板函数定义如上;
如果对象中有需要赋值的变量,那么可以理解为先执行构造函数进行赋值,该值写在括号里就行,然后再利用该函数对象进行判断求值。
示例:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
template<class T>
class sum{
int num;
public:
sum(int i):num(i){ }
T operator()(T &total,const T &value){
for(int n = 1;n <= num;n++)
total += value;
return total;
}
};
int comp(int total,int value)
{
return total + value * 4;
}
int main()
{
const int SIZE = 10;
int a[] = { 1,2,3,4,5,6,7,8,9,10 };
vector<int> v(a,a + SIZE);
int _sum = accumulate(v.begin(),v.end(),0,sum<int>(6));
cout<<_sum<<endl;
_sum = accumulate(v.begin(),v.end(),0,comp);
cout<<_sum<<endl;
return 0;
}
执行效果
函数对象的 operator() 成员函数可以根据对象内部的不同状态执行不同操作,而普通函数就无法做到这一点。因此函数对象的功能比普通函数更强大。
那么构造函数和函数对象调用如何区分呢?
需要类所定义胡对象单独调用函数即可。
STL 的< functional > 其他对象模板
如大小比较模板greater 的应用
- list 有两个sort函数,不带参数的sort函数
lst2.sort();
,它将list中的元素 按 < 规定的比较方法升序排列。 - list还有另一个sort函数: template void sort(T2 op); 可以用 op来比较大小,即 op(x,y) 为true则认为x应该排在前面。
其中copy类似于
template <class T1,class T2>
void Copy(T1 s,T1 e, T2 x) {
for(; s != e; ++s,++x)
*x = *s;
}
排序規則
- 返回 true,意味着 a1 必须在 a2 前面
- 返回 false,意味着 a1 并非必须在 a2 前面
- 排序规则的写法,不能造成比较 a1,a2 返回 true 比较 a2,a1 也返回 true 否则sort会 runtime error
- 比较 a1,a2 返回 false 比较 a2,a1 也返回 false,则没有问题
#include <iostream>
#include <iterator>
#include<vector>
using namespace std;
class Less {
public:
bool operator ()(int a1, int a2)
{
if (a1 < a2)
return true;
else
return false;
}
};
bool comp(int a1, int a2)
{
if (a1 < a2)
return false;
else
return true;
}
template<typename T,typename pr>
T _max(T* p, int n, pr _less)
{
T temp = p[0];
for (int i = 0; i < n; i++)
if (_less(temp, p[i]))
temp = p[i];
return temp;
}
int main()
{
int a[] = { 1,2,6,4,2,3,4,8,5 };
cout << _max(a, 9, Less()) << endl;
cout << _max(a, 9, comp);
return 0;
}
执行效果