仿函数(Functors,Function Objects)
定义:
我们都清楚普通函数的定义与使用方法,我们可以说任何东西,只要其行为像函数,它就是个函数。如果我们定义了一个对象,其行为像函数,它就可以被当函数来使用。所谓函数行为,是指可以“使用小括号传递参数,蔚以调用某个东西”。例如:
function(arg1, arg2);//a function call
所谓仿函数,又叫函数对象,是一个定义operator()的对象。例如:
FunctionObjecType FunctorObj;
...
FunctorObj(...);
其中表达式FunctorObj()将会调用FunctorObj的operator()实现,即进行了操作符的重载,而非调用FunctorObj()。函数对象概念包括Generator, Unary Function(一元函数), 和Binary Function(二元函数),分别可以f(),f(x),f(x, y)的形式调用。返回bool类型的仿函数比较特殊,比如返回bool的Unary Function叫做Predicate(判断式),返回bool的Binary Function叫做Binary Predicate。
Predicate就经常应用于STL算法中,一些算法可以接受用户定义的的辅助函数,提高了算法的灵活性。比较经常应用的场景就是指定排序准则和搜寻准则。
一个类的使用看上去像一个函数,它是通过通过重载类的()操作符来实现的。如下:
例子1
class CPrintInt
{
public:
void operator()(int nElements)const//使用重载()来实现
{
cout <<"call operator():"<< nElements << endl;
}
};
CPrintInt CPrint;
CPrint(10);//像函数一样去使用
CPrint.operator()(10);//显示调用
优势
仿函数应用
排序准则(Sort Criteria)
class PersonInfo
{
public:
PersonInfo(const string&strFirstName, const string& strSecondName)
{
m_strFirstName = strFirstName;
m_strSecondName = strSecondName;
}
string& GetFirstName()
{
return m_strFirstName;
}
string& GetSecondName()
{
return m_strSecondName;
}
public:
string m_strFirstName;
string m_strSecondName;
};
class PersonSortCriterion
{
public:
bool operator()(const PersonInfo& Per1, const PersonInfo &Per2)
{
//a person is less than another person
//if the second name is less
//if the second name is equal and the first name is less
return ((Per1.m_strSecondName < Per2.m_strSecondName) ||
(!(Per2.m_strSecondName < Per1.m_strSecondName) &&
Per1.m_strFirstName < Per2.m_strFirstName));
}
};
/****************************************************************
*函数名称:FunctorSortCriteria
*功 能:仿函数应用于排序准则
*作 者:Jin
*日 期:2016年6月17日
****************************************************************/
void FunctorSortCriteria()
{
typedef set<PersonInfo, PersonSortCriterion> PersonSet;
PersonSet PersonNamesInfo;
PersonInfo p1("xu", "jin");
PersonInfo p2("huang", "jin");
PersonInfo p3("hua", "tong");
PersonInfo p4("huang", "shu");
PersonInfo p5("xu", "wei");
PersonNamesInfo.insert(p1);
PersonNamesInfo.insert(p2);
PersonNamesInfo.insert(p3);
PersonNamesInfo.insert(p4);
PersonNamesInfo.insert(p5);
//print user define sort criterion
PersonSet::iterator it = PersonNamesInfo.begin();
for (; it != PersonNamesInfo.end(); it++)
{
cout << "FirtName:" << it->GetFirstName() << "\t"
<< "SecondName: " << it->GetSecondName()
<< endl;
}
}
运行结果:
拥有内部状态(Internal State)
/****************************************************************
*函数名称:HoldStateByValue
*功 能:模拟仿函数在同一个时刻下拥有多个状态(多个函数实体)
*作 者:Jin
*日 期:2016年6月5日
****************************************************************/
void HoldStateByValue()
{
list<int> Coll;
//Note:make sure container has enough capcity or use insert itertor
// when you are using generate_n or generat algorithm
//insert value from 1 to 9,
generate_n(back_inserter(Coll), //start
9, //number of element
IntSequence(1)); //generator values,initial value 1 functor obj
//output: 1 2 3 4 5 6 7 8 9
PrintElements(Coll);
//replace second to last element,starting at 42
generate(++Coll.begin(),
--Coll.end(),
IntSequence(42)); //initial value 42,functor obj
//output:1 42 43 44 45 46 47 48 9
PrintElements(Coll);
}
InitSequence seq(1);
//insert value beginning with 1,by pass ,change internal state
generate_n(back_inserter(coll), 9, seq)
//insert value beginning with 1
generate_n(back_inserter(coll), 9, seq)
我们改变的是仿函数副本,不能获取对象最终状态,但是有个好处是我们可以传递常量或者暂时表达式,缺点就是不能得到“最终结果”或者“反馈”。现在有两种办法可以从中获取其“最终结果”。方式一是by reference ,方式二是运用for_each算法。//Predefined algorithm is not by reference, so user defines it
// generate_n<back_insert_iterator<list<int> >,
// int,
// IntSequence&>(back_inserter(Coll), 4, SeqValue);
template<class _OutIt, class _Diff, class _Fn0>
void GenSequenceUser(_OutIt _Dest, _Diff _Count, _Fn0 &_Func)//by reference
{
for (; 0 < _Count; --_Count, ++_Dest)
{
*_Dest = _Func();
}
}
/****************************************************************
*函数名称:HoldStateByReference
*功 能:仿函数的by reference特性
*作 者:Jin
*日 期:2016年6月5日
****************************************************************/
void HoldStateByReference()
{
list<int> Coll;
IntSequence SeqValue(1);
//function object By reference ,so m_nValue will continue with 6 in the SeqVale
//insert 1 2 3 4 5
GenSequenceUser(back_inserter(Coll), 5, SeqValue);
//first output:1 2 3 4 5
PrintElements(Coll, "first time insert value: ");
//initial m_nValue is 6,so will insert 6 7 8 9 10
generate_n(back_inserter(Coll), 5, SeqValue);
//second output:1 2 3 4 5 6 7 8 9 10
PrintElements(Coll, "second time insert value: ");
//function object By pass, so that m_nValue initial value doesn't change,it is 6 ;
//insert 6 7 8 9 10
generate_n(back_inserter(Coll), 5, SeqValue);
//output: 1 2 3 4 5 6 7 8 9 10 6 7 8 9 10
PrintElements(Coll ,"third time inset value: ");
//creat other function object instance By pass,
//insert 20 21
generate_n(back_inserter(Coll), 2, IntSequence(20));
//output: 1 2 3 4 5 6 7 8 9 10 6 7 8 9 10 20 21
PrintElements(Coll ,"fourth time inset value: ");
}
class CMeanValue
{
public:
//constructor
CMeanValue():lNum(0),lSum(0){};
//function call
void operator()(int elem)
{
lNum += 1;
lSum += elem;
}
//return mean value
double GetMeanValue()
{
return static_cast<double>(lSum) / static_cast<double>(lNum);
}
private:
long lNum;//number of elements
long lSum;//sum of all elements
};
/****************************************************************
*函数名称:GetFunctorSatate
*功 能:利用for_each返回值,获取仿函数结果
*作 者:Jin
*日 期:2016年6月5日
****************************************************************/
void GetFunctorSatate()
{
list <int> Coll;
const int nMaxNum= 10;
for (int i = 0;i < nMaxNum; i++)
{
Coll.push_back(i);
}
CMeanValue obj;
CMeanValue Ret;
//for_each return Functor after deal all the elements
Ret= for_each(Coll.begin(),Coll.end(), obj/*CMeanValue()*/);
cout << "mean value: " << Ret.GetMeanValue() << endl;
}
仿函数(functor)在各编程语言中的应用
#include "stdafx.h"
#include <string.h>
#include <algorithm>
#include <vector>
#include <deque>
#include <functional>
#include <iostream>
#include <list>
#include <sstream>
#include <iterator>
#include <functional>
#include <stdlib.h>
using namespace std;
/************************************************************************/
/* 比较两个参数大小,确定排序规则 */
/* 返回值: */
/* > 0 a排在b后面 */
/* = 0 排序不确定 */
/* < 0 a排在b前面 */
/************************************************************************/
int CompareAge(const void *a, const void *b)
{
return *((int*)(a)) - *((int*)(b));
}
//define less function object
class CUseLess
{
public:
bool operator()(int a, int b)const
{
return a < b;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
const int MaxNum = 5;
int nAge[MaxNum] = {10, 12, 30, 4, 27};
//C语言使用函数指针和回调函数来实现仿函数
printf("Use function point and Callback function\n");
qsort(nAge, sizeof(nAge)/sizeof(0), sizeof(int), CompareAge);
for (int i = 0; i < MaxNum; i++)
{
printf("%d ",nAge[i]);
}
printf("\n");
//C++中,使用在一个类中重载括号运算符的方法,使一个类对应具有函数行为
vector <int> vecCollector;
for (int i = 0; i < MaxNum; ++i)
{
vecCollector.push_back(nAge[i]);
}
//其实sort排序算法中,我们经常会引用“functional”中预定义的仿函数,
//诸如less,greater,greater_equal等,这里只是使用用户定义版的less,实现原理是一样的。
//sort(vecCollector.begin(), vecCollector.end(), less<int>());//functional中的仿函数
cout << "Use Fucntion object" << endl;
sort(vecCollector.begin(), vecCollector.end(), CUseLess()); //用户自定义的仿函数
copy(vecCollector.begin(), vecCollector.end(), ostream_iterator<int>(cout," "));
cout << endl;
return 0;
}
输出:
预定义的仿函数
STL中提供了许多预定义的仿函数,要使用这些预定义的仿函数必须包含头文件<functional>,,对于对对象排序或进行比较时,一般都是以less<>为预设准则。表1列出了这些仿函数。