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的数据个数是多少,可知符合条件的数
有{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”,是正确的。
- STL--函数对象
- STL函数对象
- STL之函数对象
- STL 函数对象
- [STL基础]函数对象
- STL 函数对象
- STL中的函数对象
- STL中的函数对象
- STL函数对象
- STL源码--函数对象
- STL函数对象
- STL之函数对象
- STL--函数对象学习
- STL之函数对象
- STL 函数对象
- STL函数对象
- STL函数对象2
- STL算法--------函数对象
- Knight Moves bfs
- 大姚土豆丝
- atol()函数一个可移植的版本
- 城堡(简单但费时)
- 60个很实用的jQuery代码开发技巧收集
- STL 函数对象
- C语言 键盘编码 及 用法
- 侃侃Thinking In Java
- NYOJ 811 变态最大值
- RHEL7 USB installation problem and solving
- Discuz! System Error,您当前的访问请求当中含有非法字符
- 1.快恶心死了才把BBB(REVC)gpioLedExample弄出来,重点是裸机环境构建啦!!
- 【读书笔记-数据挖掘概念与技术】挖掘频繁模式、关联和相关性:基本概念和方法
- 黑马程序员_【总结】_ 高新_3_泛型和类型参数