函数对象
函数对象是重载了operator()的类的一个实例,operator()是函数调用运算符。标准C++库根据operator()参数个数为0个,1个,2个加以划分的。主要有以下3种类型:
发生器:一种没有参数且返回一个任意类型值的函数对象,例如随机数发生器。
一元函数:一种只有一个任意类型的参数,且返回一个可能不同类型值的函数对象。
二元函数:一种有两个任意类型的参数,且返回一个任意类型值的函数对象。
一元判定函数:返回bool型值的一元函数
二元判定函数: 返回bool型值的二元函数
<span style="font-size:18px;">#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<vector>
using namespace std;
int sum=0;
void f(int n)
{
sum+=n;
}
int main()
{
vector<int>v;
for(int i=0;i<=100;i++)
{
v.push_back(i);
}
for_each(v.begin(),v.end(),f);
printf("sum=%d\n",sum);
return 0;
}</span>
<span style="font-size:18px;">#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
template<class T>
class CSum
{
private:
T sum;
public:
CSum()
{
sum=0;
}
void f(T n)
{
sum+=n;
}
T GetSum()
{
return sum;
}
};
int main()
{
CSum<int>obj;
for(int i=0;i<=100;i++)
{
obj.f(i);
}
cout<<obj.GetSum()<<endl;
return 0;
}</span>
求整型向量各元素之和<span style="font-size:18px;">#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
class CSum
{
private:
int sum;
public:
CSum()
{
sum=0;
}
void operator()(int n)
{
sum+=n;
}
int GetSum()
{
return sum;
}
};
int main()
{
vector<int>v;
for(int i=0;i<=100;i++)
{
v.push_back(i);
}
CSum obj=for_each(v.begin(),v.end(),CSum());
cout<<obj.GetSum()<<endl;
return 0;
}</span>
必须重载operator()函数:这是实现函数对象功能最重要的环节,不能随便写,因此6.1.1节中CSum类中的f(int)函数修改为operator()(int),而
函数体的内容不变。
函数对象调用方式:直接采用构造函数调用,如本例中for_each(v.begin(), v.end(), CSum())中的第3个参数CSum(),也就是说:对本例而
言,STL知道CSum()对应着CSum类中重载的operator()函数,具体有几个参数呢,由于for_each函数一次只能迭代出一个整形数,所以
STL知道每迭代一次整形数,都要执行一次CSum中的operator()(int)函数。
CSum Obj是用来接收for_each迭代函数对象的最终结果值的。
一元函数
STL中一元函数基类是一个模板类,原形如下:
template<class _A, class _R>
struct unary_function
{ typedef _A argument_type;
typedef _R result_type;
};
它有两个模板参数,_A是输入参数,_R是返回类型,且此两个参数的类型是任意的,因此,它的动态特性非常强。
利用一元函数求向量各元素之和。
<span style="font-size:18px;">#include<iostream>
#include<algorithm>
#include<functional>
#include<vector>
using namespace std;
template<class _inPara,class _outPara>
class CSum:public unary_function< _inPara,_outPara>
{
public:
_outPara sum;
CSum(_outPara init=0)
{
sum=init;
}
void operator()(_inPara n)
{
sum+=n;
}
_outPara GetSum()
{
return sum;
}
};
int main()
{
vector<int>v;
for(int i=1;i<=100;i++)
{
v.push_back(i);
}
CSum<int,int>obj=for_each(v.begin(),v.end(),CSum<int,int>(100)); //有初始值
cout<<obj.GetSum()<<endl;
vector<float>v2;
float f=1.3f;
for(int i=1;i<=100;i++)
{
v2.push_back(f);
f+=1.0f;
}
CSum<float,float>obj2=for_each(v2.begin(),v2.end(),CSum<float,float>());
cout<<obj2.GetSum()<<endl;
return 0;
}</span>
主要理解以下几点。
(1)应用STL模板一元函数必须从unary_function基类派生。例如本例中的CSum类。
(2)加深对一元函数模板类模板参数的理解。例如本例中的_inPara表示的是operator()函数的参数类型,所以写作voidoperator()(_inPara
n),_outPara表示的是返回值的类型,因此返回值变量sum应定义成_outPara类型。由于此两个参数在本例中都是动态传进去的,因此在
类定义的前面要加上template<class _inPara, class _outPara>。
(3)对调用模板函数对象方式的理解。例如本例中CSum<int,int> sObj=for_each(v.begin(), v.end(), CSum<int, int>())表明是对整形向量元素
求和,CSum<int,int>表明CSum是一个模板类,两个动态参数都是整形数;CSum<float,float>sObj2=for_each(v2.begin(), v2.end(),
CSum<float, float>())表明是对浮点向量元素求和,两个动态参数都是浮点型。可以看出对整形向量求和、浮点向量求和都是由CSum函
数对象类完成的,当然也可用CSum求其它数据类型的和,从中可以体会出STL中一元函数模板类功能的强大。
二元函数
STL中二元函数基类是一个模板类,原形如下所示。
template<class Arg1, class Arg2, class Result>
struct binary_function
{
typedef Arg1first_argument_type;
typedef Arg2second_argument_type; typedef Resultresult_type;
};
它有三个模板参数,Arg1、Arg2是输入参数,Result是返回类型,且此三个参数的类型是任意的,因此,它的动态特性非常强。
利用二元函数使学生成绩升序排列
<span style="font-size:18px;">#include<functional>
#include<algorithm>
#include<iostream>
#include<vector>
#include<iterator>
#include<string>
using namespace std;
class Student
{
public:
string name;
int grade;
public:
Student(string nam,int grad):name(nam),grade(grad)
{
}
bool operator<(const Student &s)const
{
return grade<s.grade;
}
};
ostream &operator<<(ostream &os,const Student &s)
{
os<<s.name<<"\t"<<s.grade<<"\t"<<endl;
return os;
}
template<class _inpara,class _inpara2>
class binary_sort:public binary_function<_inpara,_inpara2,bool>
{
public:
bool operator()(_inpara in1,_inpara2 in2)
{
return in1<in2;
}
};
int main()
{
Student s1("张1",60);
Student s2("张2",80);
Student s3("张3",70);
Student s4("张4",90);
vector<Student>v;
v.push_back(s1);
v.push_back(s2);
v.push_back(s3);
v.push_back(s4);
sort(v.begin(),v.end(),binary_sort<const Student&,const Student &>());
copy(v.begin(),v.end(),ostream_iterator<Student>(cout,""));
cout<<endl;
for(int i=0;i<v.size();i++)
{
cout<<v.at(i);
}
cout<<endl;
vector<Student>::iterator it=v.begin();
while(it !=v.end())
{
cout<<*it;
it++;
}
return 0;
}</span>
系统函数对象
主要包含算术类、关系运算类、逻辑运算类三种函数对象。
算术类基本函数对象使用示例
<span style="font-size:18px;"><span style="font-size:18px;">#include<functional>
#include<iostream>
using namespace std;
int main()
{
//以下产生函数实体
plus<int>plusobj;
minus<int>minusobj;
multiplies<int>mulobj;
divides<int>dividesobj;
modulus<int>modobj;
negate<int>negobj;
//以下运用上述对象,履行函数功能
cout<<plusobj(2,4)<<endl;
cout<<minusobj(2,4)<<endl;
cout<<mulobj(2,4)<<endl;
cout<<dividesobj(2,4)<<endl;
cout<<modobj(5,3)<<endl;
cout<<negobj(4)<<endl;
//以下直接用函数对象的临时对象履行函数功能
//functional<T>() 是一个临时对象,调用operator运算符
cout<<endl<<endl;
cout<<plus<int>()(2,4)<<endl;
cout<<minus<int>()(2,4)<<endl;
cout<<multiplies<int>()(2,3)<<endl;
cout<<divides<int>()(2,4)<<endl;
cout<<modulus<int>()(5,3)<<endl;
cout<<negate<int>()(4)<<endl;
return 0;
}</span></span>
非常规数据类型基本函数对象使用示例
<span style="font-size:18px;">#include<iostream>
#include<functional>
#include<vector>
#include<numeric>
using namespace std;
class Complex
{
public:
float real;
float virt;
public:
Complex()
{
}
Complex(float r,float v):real(r),virt(v)
{
}
Complex operator+(const Complex &s)const
{
Complex v;
v.real=real+s.real;
v.virt=virt+s.virt;
return v;
}
};
ostream &operator<<(ostream &os,Complex &s)
{
os<<s.real<<"+"<<s.virt<<"i"<<endl;
return os;
}
int main()
{
Complex c1(1,2);
Complex c2(2,3);
Complex c3=c1+c2;
Complex c4=plus<Complex>()(c1,c2);
Complex c;
vector<Complex>v;
v.push_back(c1);
v.push_back(c2);
v.push_back(c3);
v.push_back(c4);
Complex result=accumulate(v.begin(),v.end(),c,plus<Complex>());
cout<<result;
return 0;
}</span>
关系运算类基本函数对象使用示例
<span style="font-size:18px;">#include<functional>
#include<iostream>
using namespace std;
int main()
{
equal_to<int>equalobj;
not_equal_to<int>noequalobj;
greater<int>greaterobj;
less<int>lessobj;
less_equal<int>less_equalobj;
cout<<equalobj(2,4)<<endl;
cout<<noequalobj(2,4)<<endl;
cout<<greaterobj(2,4)<<endl;
cout<<lessobj(2,4)<<endl;
cout<<less_equalobj(2,4)<<endl;
cout<<endl<<endl;
cout<<equal_to<int>()(2,4)<<endl;
cout<<not_equal_to<int>()(2,4)<<endl;
cout<<greater<int>()(2,4)<<endl;
cout<<greater_equal<int>()(2,4)<<endl;
cout<<less<int>()(2,4)<<endl;
cout<<less_equal<int>()(2,4)<<endl;
return 0;
}</span>
对常规通用数据类型char, int, float,string而言,可以直接按上述写法就可以了。但是对非常规数据类型,则必须重载类
中的各个operator关系运算符。
利用二元函数比较两复数是否相等问题。
#include<functional>
#include<iostream>
using namespace std;
class Complex
{
public:
float real;
float virt;
public:
Complex()
{
this->real=0;
this->virt=0;
}
Complex(float r,float v):real(r),virt(v)
{
}
bool operator==(const Complex &c)const
{
return ((real==c.real)&&(virt==c.virt));
}
};
int main()
{
Complex c1(1,2);
Complex c2(2,3);
Complex c3(3,4);
cout<<equal_to<Complex>()(c1,c2)<<endl;
cout<<equal_to<Complex>()(c1,c3)<<endl;
return 0;
}
主要理解equal_to的流程。当执行到equal_to<Complex>()(c1, c2)时,首先调用二元函数类equal_to中重载的operator= =运算符函数。由于Complex是非通用数据类型,之后接着
调用Complex中重载的operator= =运算符函数,完成真正的复数比较,布尔值依次返回。
逻辑运算类函数对象基本用法
#include<functional>
#include<iostream>
using namespace std;
int main()
{
logical_and<int>andobj;
logical_or<int>orobj;
logical_not<int>notobj;
cout<<andobj(true,true)<<endl;
cout<<orobj(true,false)<<endl;
cout<<notobj(true)<<endl;
cout<<endl<<endl;
cout<<logical_and<int>()(3<5,6<9)<<endl;
cout<<logical_or<int>()(3<5,6<9)<<endl;
cout<<logical_not<int>()(3<5)<<endl;
return 0;
}
函数适配器
函数适配器主要有以下几方面的主要优点:
(1)可以形成复杂的,有语义的表达式。上文讲述的算术类、关系类、逻辑运算类函数对象功能已经很强大了,但某些时候仍显不足,
例如:求某整形数组a中大于10数的个数。很明显可知:若用系统提供的函数对象,那么一定是greater,可是下述写法又无法体现出比较
的数10: int n =count_if(a,a+sizeof(a)/sizeof(int),greater<int>())这种写法是错误的,利用适配器可以很好的解决这种问题。
(2)可以调用类中普通的成员函数。我们熟知:STL中绝大多数算法归根结底是调用功能类中重载的operator运算符来实现的,然而,功
能类中还有许多普通的成员函数,STL本身不能直接调用,必须经过适配器转换,才可调用。
绑定、取反适配器基本用法
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
int main()
{
int a[]={1,3,5,7,9,8,6,4,3,2,0};
int nCount=count_if(a,a+sizeof(a)/sizeof(int),bind2nd(less<int>(),4));
cout<<nCount<<endl;;
nCount=count_if(a,a+sizeof(a)/sizeof(int),bind1st(less<int>(),4));
cout<<nCount<<endl;
nCount=count_if(a,a+sizeof(a)/sizeof(int),not1(bind2nd(less<int>(),4)));
cout<<nCount<<endl;
nCount=count_if(a,a+sizeof(a)/sizeof(int),not1(bind1st(less<int>(), 4)));
cout<<nCount<<endl;
sort(a,a+sizeof(a)/sizeof(int),not2(less<int>()));
copy(a,a+sizeof(a)/sizeof(int),ostream_iterator<int>(cout," "));
return 0;
}
(1)bind2nd(less<int>(),4)):less<int>()本身是一个二元函数对象,相当于普通函数boolless(T x,Ty){ return x<y;} ; 而bind2nd(less<int>(),4))
相当于普通函数boolless(T x, intn=4){return x<4},bind2nd的作用是使less二元函数的第二个参数绑定为整形4。因此相当于把二元函数降
低为一元函数,count_if语句中是求数组a中小于4的数据个数是多少,可知符合条件的数有{1,3,2,0},共4个。
(2)bind1st(less<int>(),4):less<int>()本身是一个二元函数对象,相当于普通函数boolless(T x,Ty){ return x<y;} ; 而bind1st(less<int>(),4))相当
于普通函数boolless(intn=4, T x){return 4<x},bind1st的作用是使less二元函数的第1个参数绑定为整形4。因此相当于把二元函数降低为一
元函数,count_if语句中是求数组a中大于4的数据个数是多少,可知符合条件的数有{5,7,9,8,6},共5个。
(3)not1(bind2nd(less<int>(),4)):我们知道bind2nd(less<int>(),4)相当于普通函数boolless(T x,intn=4){return x<4;},not1后,相当于
boolless(T x, intn=4){return !(x<4);},语义上相当于“不小于4”,即求不小于4的数据个数是多少,可知符合条件的数
有{5,7,9,8,6,4},共有6个。
(4)not1(bind1st(less<int>(),4)):我们知道bind1st(less<int>(),4)相当于普通函数boolless(intn=4, T x){return4<x;},not1后,相当于
boolless(intn=4, T x){return !(4<x);},语义上相当于“不大于4”,即求不大于4的数据个数是多少,可知符合条 件的数
有{1,3,4,2,0},共有5个。
(5)not2(less<int>()):我们知道less<int>()相当于普通函数boolless(T x, T y){return x<y},not2后相当于boolless(Tx,T y){return !(x<y);},即求
数组a的降幂排序序列。
成员函数适配器基本用法
<span style="font-size:14px;">#include<functional>
#include<algorithm>
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Student
{
string strNO;
string strName;
public:
Student(string NO,string name):strNO(NO),strName(name){}
bool show()
{
cout<<strNO<<":"<<strName<<endl;
return true;
}
};
int main()
{
//针对mem_fun_ref程序段
Student s1("1001","张三");
Student s2("1002","张四");
vector<Student>v;
v.push_back(s1);
v.push_back(s2);
for_each(v.begin(),v.end(),mem_fun_ref(Student::show));
//针对mem_fun程序段
Student *ps1=new Student("1003","张武");
Student *ps2=new Student("1004","张路");
vector<Student*>pv;
pv.push_back(ps1);
pv.push_back(ps2);
for_each(pv.begin(),pv.end(),mem_fun(Student::show)); //我编译通不过???
return 0;
}</span><span style="font-size:12px;">
</span>
(1)mem_fun_ref、mem_fun的区别:若集合是基于对象的,形如vector<Student>,则用mem_fun_ref;若集合是基于对象指针
的,形如vector<Student*>,则用mem_fun。
(2)以下调用写法是错误的:for_each(v.begin(),v.end(),Student::show), 当在STL算法中调用成员函数时,一般要通过
mem_fun_ref或mem_fun转换后才可以应用。
<span style="font-size:14px;">#include <iostream>
#include<functional>
#include<algorithm>
using namespace std;
bool f(int x)
{
return x>3;
}
bool g(int x,int y)
{
return x>y;
}
int main()
{
int a[]={2,5,3,7,1,9,8,0,6};
int nSize=sizeof(a)/sizeof(int);
int nCount=count_if(a,a+nSize,f);
cout<<nCount<<endl;
nCount=count_if(a,a+nSize,ptr_fun(f));
cout<<nCount<<endl;
nCount=count_if(a,a+nSize,bind2nd(ptr_fun(g),3));
cout<<nCount<<endl;
nCount=count_if(a,a+nSize,bind2nd(ptr_fun(g),5));
cout<<nCount<<endl;
return 0;
}</span><span style="font-size:18px;">
</span>
(1)第1个、第2个count_if语句中调用了普通函数f(x),f(x)中把比较条件固定死了,即只能求大于3的数据个数,第2个count_if中普通函数
用适配器ptr_fun(f)加以修饰,第1个count_if中直接使用f做参数,但它们的输出结果是相同的,都是5。所以对一个参数的普通函数而
言,使用ptr_fun适配器与否并不能明显看出它的优势。
(2)第3个、第4个count_if语句中调用了普通函数g(x,y)。用bind2nd(ptr_fun(g),3)等效于调用g(x,3),用bind2nd(ptr_fun(g),5)等效于调用
g(x,5),表明分别求大于3,大于5的数据个数,它是通过先用ptr_fun(g)适配器修饰,再通过绑定适配器bind2nd把g(x,y)函数的第2个
参数固定为3、5来实现的,这样的好处是动态性比较强。因此,这种情况下,把二元普通函数用ptr_fun适
配是必须的。
<span style="font-size:14px;">#include<functional>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
class Shape
{
public:
virtual bool ShowArea()=0;
};
class Circle:public Shape
{
float r;
public:
Circle(float R):r(R){}
bool ShowArea()
{
cout<<3.14*r*r<<endl;
return true;
}
};
class Rectangle:public Shape
{
float width,height;
public:
Rectangle(float w,float h):width(w),height(h){}
bool ShowArea()
{
cout<<width*height<<endl;
return true;
}
};
class AreaCollect
{
vector<Shape *>v;
public:
bool Add(Shape *p)
{
v.push_back(p);
return true;
}
bool ShowArea()
{
vector<Shape *>::iterator it=v.begin();
// for_each(v.begin(),v.end(),mem_fun(Shape::ShowArea));
while (it!=v.end())
{
(**it).ShowArea();
it++;
}
cout<<endl;
return true;
}
};
int main()
{
AreaCollect contain;
Shape *pobj1=new Circle(10);
Shape *pobj2=new Rectangle(10,20);
contain.Add(pobj1);
contain.Add(pobj2);
contain.ShowArea();
return 0;
}
</span>
#include<functional>
#include<algorithm>
#include<iostream>
#include<vector>
#include<iterator>
#include<string>
using namespace std;
class Student
{
public:
string strNO;
int Grade;
public:
Student(string NO,int G):strNO(NO),Grade(G){}
};
class StudentIndex:public binary_function<int,int,bool>
{
vector<Student>&vs;
public:
StudentIndex(vector<Student>& vstu):vs(vstu){}
bool operator()(int a,int b)
{
return vs.at(a).Grade<vs.at(b).Grade;
}
};
int main()
{
Student s1("1001",70);
Student s2("1002",60);
Student s3("1003",80);
Student s4("1004",74);
vector<Student>v;
v.push_back(s1);
v.push_back(s2);
v.push_back(s3);
v.push_back(s4);
vector<int>v2;
v2.push_back(0);
v2.push_back(1);
v2.push_back(2);
v2.push_back(3);
sort(v2.begin(),v2.end(),StudentIndex(v));
copy(v2.begin(),v2.end(),ostream_iterator<int>(cout," "));
return 0;
}
(1)学生基本信息类Student非常简单,成员变量仅包含学号及成绩。
(2)StudIndex是一个标准的二元函数类。成员变量vector<Student>&vStud定义了学生集合对象的引用变量,它是通过构造函数
StudIndex(vector<Student>&vStud)加以初始化的。运算符函数booloperator()(inta, intb)是真正被调用的二元函数实现体。
(3)main是一个测试函数。先定义了4个学生对象,把它们依次添加到集合类v中;之后索引为0开始,依次递增为1,形成这4个学生对象
的索引集合vIndex;最后通过sort函数,依据成绩升序信息,对索引集合vIndex排序,结果为“10 3 2”,是正确的。