函数对象 / 仿函数
重载函数调用操作符的类,其对象 常称为函数对象(function object) ,也叫仿函数(functor) ,使得类对象可以像函数那样调用。
STL提供的算法往往有两个版本,一种是按照我们常规默认的运算来执行,另一种允许用户自己定义一些运算或操作,通常通过回调函数或模版参数的方式来实现,此时functor便派上了用场,特别是作为模版参数的时候,只能传类型。
函数对象超出了普通函数的概念,其内部可以拥有自己的状态(其实也就相当于函数内的static变量),可以通过成员变量的方式被记录下来。
函数对象通常不定义构造和析构函数,所以在构造和析构时不会发生任何问题,避免了函数调用时的运行时问题。
模版函数对象使函数对象具有通用性,这也是它的优势之一。
STL需要我们提供的functor通常只有一元和二元两种。
谓词
返回值为bool
的普通函数或者函数对象,也就是我们离散数学中学习的predictor,比较常用的是一元谓词和二元谓词。
在概述中有提到过,这里再重复一下。
内建函数对象
使用时需要包含头文件<functional>
STL 内建了一些函数对象,分为:
算术类函数对象
复制 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 > ; // 取反函数
negate 是一元运算,其他都是二元运算。
关系运算类函数对象
复制 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 > ; // 小于等于
逻辑运算类运算函数
复制 template < class T > bool logical_and < T > ; // 逻辑与
template < class T > bool logical_or < T > ; // 逻辑或
template < class T > bool logical_not < T > ; // 逻辑非
适配器
为了方便理解,我们以例子的形式来讲述这个部分的内容。
函数对象适配器
原程序
复制 #include <iostream>
#inlcude < algorithm >
#include <vector>
using namespace std;
class myPrint
{
public :
void operator ()( int val) { cout << val << endl; }
}
int main ()
{
vector <int> v;
for ( int i = 0 ; i < 10 ; i ++ ) v . push_back (i);
for_each ( v . begin () , v . end () , myPrint ());
return 0 ;
}
新需求
我们希望在每个数据输出的时候加上一个基值,并且该基值由用户输入。
使用函数适配器做的改进
复制 #include <iostream>
#inlcude < algorithm >
#include <vector>
#include <functional>
using namespace std;
class myPrint : public binary_function < int , int , void >
// 2.做继承 参数1类型 + 参数2类型 + 返回值类型 binary_function
{
public :
void operator ()( int val , int base) const // 3. 加const, 和父类保持一致
{
cout << val + base << endl;
}
}
int main ()
{
vector <int> v;
for ( int i = 0 ; i < 10 ; i ++ ) v . push_back (i);
int n;
cin >> n;
for_each ( v . begin () , v . end () , bind2nd ( myPrint () , n));
// 1. 将参数进行绑定 bind2nd
// bind1st 功能类似,不过n会被绑定到第一个参数中
return 0 ;
};
取反适配器
原程序
复制 #include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class GreaterThanFive
{
public :
bool operator ()( int val) { return val > 5 ; }
}
int main ()
{
vector <int> v;
for ( int i = 0 ; i < 10 ; i ++ ) v . push_back (i);
vector< int > :: iterator pos = find_if ( v . begin () , v . end () , GreaterThanFive ());
if (pos != v . end ()) cout << * pos << endl;
return 0 ;
}
新需求
我们希望找第一个不大于5的数,但又不想再写一个LessEqualThanFive。
使用取反适配器做的改进
复制 #include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class GreaterThanFive : public unary_function < int , bool >
// 2. 做继承 参数类型 + 返回值类型 unary_function
{
public :
bool operator ()( int val) const // 3.加 const
{
return val > 5 ;
}
}
int main ()
{
vector <int> v;
for ( int i = 0 ; i < 10 ; i ++ ) v . push_back (i);
vector< int > :: iterator pos = find_if ( v . begin () , v . end () , not1 ( GreaterThanFive ())); //1. 一元取反 not1
if (pos != v . end ()) cout << * pos << endl;
return 0 ;
}
其实综合前面的内容会有更简便的方法:
复制 vector< int > :: iterator pos = find_if ( v . begin () , v . end () , not1 ( bind2nd ( greater < int >() , 5 )));
这一行代码如果还半知半解的话还请驻足思考一番,思考透彻了上述内容才算是真的懂了。
函数指针适配器
沿用函数对象适配器的例子,假设myPrint
是一个全局函数
复制 for_each ( v . begin () , v . end () , bind2nd ( ptr_fun (myPrint) , n));
// 函数指针适配器 ptr_fun 将函数指针适配成仿函数
成员函数适配器
我们假设有一个Dog
类,Dog
类内部有一个bark()
成员方法,有一个装满了Dog
的vector
叫做v
。
复制 for_each ( v . begin () , v . end () , mem_fun_ref ( & Dog :: bark));
// 成员函数适配器 mem_fun_ref
// 如果容器中存放的不是对象实体,而是对象指针时,则需使用 ptr_fun
偏函数
对于一个多参数的函数,在某些应用场景下,它的一些参数往往取固定值,可以针对这样的函数,生成一个新函数,该新函数不包含原函数中已指定固定值的参数。(partial function application, 偏函数)
例如,对于下面的print函数:
复制 void print ( int n , int base); // 按base进制来输出n
由于它常常用来按十进制输出,因此,可以基于print生成一个新函数print10,只接受一个参数n,base固定为10:
复制 #include <functional>
using namespace std;
using namespace std :: placeholders;
function <void ( int ) > print10 = bind (print , _1 , 10 );
print10 ( 23 ); //相当于 print(23, 10)
function
类和bind
的使用需要c++11
标准
至此,读者应当对于仿函数/函数对象有了一个稍微深入一点的理解了。如果还觉得有些云里雾里的话也没关系,以后熟悉了就好了。