目录
算数类函数对象:(除了 negate是一元运算,其他都是二元运算)
函数对象(重点)
函数对象,就是可以当作函数使用的对象。
类中重载了(),这个类实例化的对象叫做函数对象(仿函数)。
- 函数对象(仿函数)是一个类,不是一个函数;
- 重载了“()”操作符,使得它可以像函数一样调用;
假定某个类有一个重载的 operator(),根据需要获取的参数个数分为:
- 一元仿函数(unary functor):一个参数;
- 二元仿函数(binary functor):两个参数。
函数对象的作用:作为算法策略。
函数对象应用实例1:在 accumulate 算法中的应用
STL 中有以下实现“累加”功能的算法(函数模板),该模板的功能是对(first,last)中的每个迭代器 I 执行 val = op(val, *I),返回最终的 val。
在头文件 numeric.h 中,accumulate 的源码如下:
#include<numeric>
template <class InIt, class T, class Fn>
T accumulate(const InIt first, const InIt last, T val, Fn op)
{
for (; first != last; ++first)
val = op(val, *first);
return val;
}
此模板被实例化后,op(val, *first) 必须要有定义,则 op 只能是函数指针或函数对象。因此调用 accumulate 模板时,形参 op 对应的实参只能是函数名、函数指针或者函数对象。
eg:通过 accumulate 模板求一个 vector 中元素的平方和。
#include<numeric>
template <class T>
void PrintInterval(T first, T last)
{ //输出区间[first,last]中的元素
for (; first != last; ++first)
cout << *first << " ";
cout << endl;
}
int SumSquares(int total, int value)
{
return total + value * value;
}
template <class T>
class SumPowers
{
private:
int power;
public:
SumPowers(int p):power(p){}
const T operator()(const T& total, const T& value)
{ //计算 value 的 power 次方,并加到total上
T v = value;
for (int i = 0; i < power - 1; ++i)
{
v = v * value;
}
return total + v;
}
};
void test24()
{
const int size = 5;
int a1[] = { 1,2,3,4,5 };
vector<int> v(a1, a1 + size);
cout << "(1)";
PrintInterval(v.begin(), v.end());
int result = accumulate(v.begin(), v.end(), 0, SumSquares);
cout << "(2)平方和: " << result << endl;
result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
cout << "(3)立方和: " << result << endl;
result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));
cout << "(4)4次方和: " << result << endl;
}
注释:
1.
int result = accumulate(v.begin(), v.end(), 0, SumSquares);
第四个参数是函数名。函数名字的类型是函数指针,因此本行将 accumulate 模板实例化后得到的模板函数定义如下:
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int val, int(*op)(int, int))
{
for (; first != last; ++first)
val = op(val, *first);
return val;
}
形参 op 是一个函数指针,而 op(val, *first) 就调用了指针 op 指向的函数,即函数 SumSquares。
2.
result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
第四个参数是 SumPowes<int>(3),SumPowes 是类模板的名字,SumPowes<int> 就是类的名字。类的名字后面跟着构造函数的参数列表,就代表一个临时对象。
编译器在编译此行时,会将 accumulate 模板实例化为以下函数:
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int val, SumPowers<int> op)
{
for (; first != last; ++first)
val = op(val, *first);
return val;
}
形参 op 是一个函数对象,而 op(val, *first) 等价于:
op.operator()(val, *first);
即调用了 SumPowers<int> 类的operator 成员函数。
对比 1 和 2,函数对象的 operator() 成员函数可以根据对象内部的不同状态执行不同操作。???
函数对象应用实例2:在 sort 算法中的应用
STL 中定义了一些函数对象类模板,都位于头文件 functional 中。
eg:greater 模板的源代码如下:
template<class T>
struct greater
{
bool operator()(const T& x, const T& y)const
{
return x > y;
}
};
#include<functional>
#include<algorithm>
#include<vector>
void test18()
{
vector<int> v;
v.push_back(4);
v.push_back(7);
v.push_back(3);
sort(v.begin(), v.end(), greater<int>());
for_each(v.begin(), v.end(), [](int val) {cout << val << " "; });
//[](int val) {cout << val << " "; } //匿名函数
}
STL 中的排序模板 sort 能将区间从小到大排序。
sort算法有两个版本:
(1)该模板要求first、last是随机访问迭代器,元素比较大小是用 < 进行的。
template <class T>
void sort(const T first, const T last);
(2)该版本中,元素a、b比较大小是通过表达式 op(a,b) 进行的,op 定义了元素比较大小的规则。例如上边那个例子。
template <class T, class Pr>
void sort(const T first, const T last, Pr op);
eg:sort 算法
#include <algorithm> //sort算法在此头文件中定义
template <class T>
void Printval(T first, T last)
{
for (; first != last; ++first)
cout << *first << " ";
cout << endl;
}
class A
{
public:
bool operator<(const A& a)
{
return this->v < a.v;
}
public:
int v;
A(int n):v(n){}
};
//bool operator<(const A& a1, const A& a2) //也可以重载为A的const成员函数
//{
// return a1.v < a2.v;
//}
bool GreaterA(const A& a1, const A& a2)
{
return a1.v > a2.v;
}
struct LessA
{
bool operator()(const A& a1, const A& a2)
{
return (a1.v % 10) < (a2.v % 10);
}
};
ostream& operator<<(ostream& o, const A& a)
{
o << a.v;
return o;
}
void test25()
{
int a1[4] = { 1,5,2,7};
A a2[5] = { 43,1,8,45,62 };
sort(a1, a1 + 4);
cout << "(1) "; Printval(a1, a1 + 4);
sort(a2, a2 + 5);
cout << "(2) "; Printval(a2, a2 + 5); //按v的值从小到大排序
sort(a2, a2 + 5, GreaterA); //按v的值从大到小排序
cout << "(3) "; Printval(a2, a2 + 5);
sort(a2, a2 + 5, LessA()); //按v的个位数从小到大排序
cout << "(4) "; Printval(a2, a2 + 5);
}
注释:
1.
sort(a2, a2 + 5, GreaterA);
编译器将 sort 实例化得到的函数原型如下:
void sort(A* first, A* last, bool(*op)(const A&, const A&));
该函数在执行过程中,当要比较两元素大小时,就是看 op(a,b),本程序中 op 指向了 GreaterA,因此就用GreaterA定义的规则来比较大小。
2.
sort(a2, a2 + 5, LessA());
编译器将 sort 实例化得到的函数原型如下:
void sort(A* first, A* last, LessA op);
该函数在执行过程中,当要比较两元素大小时,就是看 op(a,b),本程序中 op(a,b) 等价于op.operator(a,b),因此就用LessA定义的规则来比较大小。
函数对象和普通函数的区别:
函数对象可以有自己的状态;(什么状态)
函数对象有类型,普通函数没类型;(怎么看的)
函数对象比普通函数执行效率高(成员函数自动申请成为内联函数);
谓词:
普通函数或重载的 operator() 返回值是 bool 类型的函数对象(仿函数)。
如果 operator 接收一个参数,则叫做一元谓词,如果接收两个参数,则叫做二元谓词。
谓词可以作为一个判断式。
内建函数对象
STL内建了一些函数对象,即函数对象类模板。
分为:算数类函数对象、关系运算类函数对象、逻辑运算类仿函数。
这些仿函数所产生的对象,用法和一般函数完全相同,当然也可以产生无名的临时对象来履行函数功能。
使用内建函数对象,需要引入头文件 #include<functional>。
算数类函数对象:(除了 negate是一元运算,其他都是二元运算)
template<class T> T plus<T> //加法仿函数
template<class T> T minus<T> //减法
template<class T> T multiplies<T> //乘法
template<class T> T divides<T> //取整
template<class T> T modulus<T> //取余
template<class T> T negate<T> //取反
关系运算类函数对象:(都是二元运算)
template<class T>bool equal_to<T> //等于
template<class T>bool not_equal_to<T> //不等于
template<class T>bool greater<T> //大于
template<class T>bool greater_equal<T> //大于等于
template<class T>bool less<T> //小于
template<class T>bool less_equal<T> //小于等于
逻辑运算类仿函数:(not 为一元运算,其余为二元运算)
template<class T>bool logical_and<T> //逻辑与
template<class T>bool logical_or<T> //逻辑或
template<class T>bool logical_not<T> //逻辑非
内建对象函数举例:
c++中struct和class的区别
C++继承了C语言的 struct,并且加以扩充。因为考虑到对C兼容,所以C++保留了 struct,并做了一些扩展使其更适合面向对象。
① C语言中,struct 是一种数据类型,只能定义数据成员,而不能定义成员函数。C++中,struct 类似于class,在其中既可以定义数据成员,也可以定义成员函数。
② 默认的继承访问权。class 默认是 private,struct 默认是 public。
③ 默认访问权限。struct 作为数据结构的实现体,它默认的数据访问权限是 public 的;而 class 作为对象的实现体,它默认的成员变量访问权限是 private 的。
可以看出,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。