STL 函数对象

来源:互联网 发布:js设置时间间隔 编辑:程序博客网 时间:2024/05/01 03:56

函数对象

函数对象是重载了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数据个数是多少,可知符合条件的数

{579864}共有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的数据个数是多少,可知符合条 件的数

{13420},共有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>


假设学生对象集合的索引从0开始,依次增1。要求不改变学生成绩集合中的学生对象顺序,依据成绩升序对索引排序


#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”,是正确的。

(4)对sort(vIndex.begin(),vIndex.end(),StudIndex(v))的理解:可看出是对学生索引集合vIndex中各元素排序,并不是对学生集合对象v排

序,符合题目要求。那么怎么对vIndex进行排序呢?根据第3个参数StudIndex(v)知,一定是调用了StudIndex中重载的booloperator()(int a

, int b)函数,a,b表示学生对象的索引序号,该函数体内容returnvStud.at(a).nGrade<vStud.at(b).nGrade返回两个学生对象成绩的比较信息

。再有StudIndex(v)另一个含义是调用StudIndex类的构造函数StudIndex(vector<Student>&vStud),把学生集合对象v传入StudIndex对象

中。

0 0
原创粉丝点击