仿函数应用详解
来源:互联网 发布:中航led软件下载 编辑:程序博客网 时间:2024/06/05 10:08
仿函数(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);//显示调用
优势
仿函数相对于普通的函数,具有更加复杂的代码设计,然而仿函数也有其过人之处,它具有如下的优点:
1、拥有状态
仿函数的能力超越了operator(),仿函数可以拥有成员函数和成员变量,这就意味着仿函数可以同时拥有状态不同的两个实体。一般函数则达不到这样的能力。
2、拥有自己的型别
我们可以将仿函数的型别当做template参数来传递,从而指定某种行为模式。容器型别也会因为仿函数的不同而不同。
3、仿函数速度更快
就template概念而言,由于很多细节在编译期就已经确定,传入一个仿函数,就可能活动更好的性能。
仿函数应用
排序准则(Sort Criteria)
我们有时候会遇到需要将object 以已序(sorted) 形式置于容器中,此时仿函数就可以派上用场了。如下:
例子2
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)
这个例子以仿函数产生一个序列,初始值可以通过仿函数的构造函数提供。同时仿函数是passed by valued(传值)
,不是passed 不用reference(传址),我们所改变的是仿函数的副本。
例如3
/*****************************************************************函数名称: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 stategenerate_n(back_inserter(coll), 9, seq)//insert value beginning with 1generate_n(back_inserter(coll), 9, seq)我们改变的是仿函数副本,不能获取对象最终状态,但是有个好处是我们可以传递常量或者暂时表达式,缺点就是不能得到“最终结果”或者“反馈”。现在有两种办法可以从中获取其“最终结果”。方式一是by reference ,方式二是运用for_each算法。
方式一:
以by reference的方式传递仿函数,我们只需要在调用算法时,明白标示仿函数是个reference型别即可。见例子4。
例子4
//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: ");}
方式二:
运用for_each()算法的返回值。如果我们使用了for_each()算法,我们可以得到for_each的返回值,来获取仿函数对象的最终状态,这是其他算法所不支持的,见例子5.
例子5class 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 objectclass 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列出了这些仿函数。
1 0
- 仿函数应用详解
- C++仿函数详解
- 仿函数 ( 函数对象 ) 详解
- 关于仿函数的应用
- c++仿函数的应用
- C++仿函数(functor)详解
- stl中的仿函数functor的应用
- 仿函数的应用(class)
- C++ STL 基础及应用(7) 函数对象(仿函数)
- 函数getopt()应用详解
- 实现大堆小堆——仿函数的应用
- STL — 仿函数的实现原理和应用
- 仿函数
- 仿函数
- 仿函数
- 仿函数
- 仿函数
- 仿函数
- mysql 常用命令集锦
- css清除浮动使父级元素展开的三个方法
- linux的read,write
- 暗淡的2015年
- C语言总结
- 仿函数应用详解
- Windows8.1配置Java环境变量
- IAR切BANK--BANK说明
- 名次预测(C语言实现)
- 通过Observer(观察者模式)来看Android的MVC
- hdoj 5607 graph 【矩阵快速幂】
- HDU 1083.Courses【二分匹配】【1月2】
- 2016CES电子展前瞻:这六大看点不容错过
- ios多线程