第六章 函数对象 系统函数对象

6.4 系统函数对象

(要求能看懂会考)

算术类函数对象:

加法:plus<T> 二元函数 结果:arg1+arg2; 减法:minus<T> 二元函数 arg1-arg2

乘法:multiples<T> 二元函数 结果:arg1*arg2;除法:divides<T>二元函数 arg1/arg2

模取:modules<T> 二元函数 结果:arg1%arg2 ; 否定: negate<T>一元函数 -arg1

关系运算类函数对象:

等于:equal_to<T> 二元函数argl==arg2 ; 不等于:not_equal_to<T> 二元函数arg1!-arg2

大于: greater<T> 二元函数arg1>arg2 ;  大于等于:greater_equal<T> 二元函数argl>=arg2

小于: less<T>   二元函数argl<arg2;   小于等于:less_equal<T> 二元函数 arg1<=arg2

逻辑运算类函数对象:

与:logical_and<T> 二元函数 arg1&.&arg2 ; 或;logical_or<T>  二元函数 arge1||arge2

非: logical_not<T> 二元函数  !Arg1

只有一个参数<T>是由于此二元函数中两个输入参数的类型相同,仅定义一个类型就可以了,而且也能够推测出返回值类型

6.4.1 算术类函数对象

 算术类基本函数对象使用示例。

法一:

//以下产生一些算术类函数对象实体

plus<int>plusbj;

minus<int>minusObj;

multiplies<int>mulobj;

//以下运用上述对象,履行函数功能

cout<<plusObj(2,4)<<endl;

cout<<minusObj(2,4)<<endl;

cout<<mulObj(2,4)<<endl;

法二:

//以下直接以函数对象的临时对象履行函数功能

//语法分析:function<T>()是一个临时对象,调用operator 运算符

cout<<plus<int>()(2,4)<<endl;

cout<<minus<int>() (2,4)<<endl;

cout<<multiplies<int>()(2,4)<<endl;

对常规通用数据类型 char、int、float、string 而言,可以直接按上述写法。

对非常规数据类型,则必须重载类中的各个operator 算术运算符。例如,复数类Complex,plus<Complex>完成两个复数加法的代码如下所示。

非常规数据类型使用:

#include <functional>//其中定义了一些函数对象和函数模板类(例如 plus 和 equal_to)

#include <iostream>

using namespace std;

class Complex {

public://实部和虚部。

    float real;

    float virt;

public://默认构造函数,将实部和虚部初始化为 0.0

    Complex() {

        this->real = 0.0f;

        this->virt = 0.0f;

    }

//构造函数,根据给定的实部和虚部初始化对象

    Complex(float real, float virt) {

        this->real = real;

        this->virt = virt;

    }

//重载了 + 运算符,用于两个复数对象的相加。返回一个新的 Complex 对象

//实部和虚部分别为两个复数对象对应部分的和

    Complex operator+ (const Complex& c) const {

        Complex result;

        result.real = real + c.real;

        result.virt = virt + c.virt;

        return result;

    }

//重载了 == 运算符,用于判断两个复数对象是否相等

    bool operator==(const Complex& c) const {

        return (real == c.real) && (virt == c.virt);

    }

};

int main() {

    Complex c1(1.0f, 2.0f);//创建一个名为 c1 的复数对象,实部为 1.0,虚部为 2.0

    Complex c2(3.0f, 4.0f);//创建一个名为 c2 的复数对象,实部为 3.0,虚部为 4.0

    Complex c3 = c1 + c2;//直接调用 Complex类中 operator+重载数且当前对象是 cl(1+2i),传对象是 2(3+4i

使用 plus 函数对象将 c1 和 c2 相加,将结果存储在 c4 中

    Complex c4 = plus<Complex>()(c1, c2);

//equal_to<Complex> 函数对象比较 c1 和 c2 是否相等,并将结果赋值给 retb

    bool retb = equal_to<Complex>()(c1, c2);//法一

    cout<<equal to<Complex>()(c1,c2)<<end1;//法二

cout << (c1 == c2) << endl;//法三



    cout << c3.real << "+" << c3.virt << "i" << endl;//输出 c3 的实部和虚部

    cout << c4.real << "+" << c4.virt << "i" << endl;

    

    

//老师上课讲 =====================================================================

Complex c1(1, 1);

Complex c2(2, 2);

//Complex result = plus<Complex>()(c1, c2);

cout << (c1 + c2) << endl;//计算 c1 和 c2 的和,并将结果输出到标准输出流

cout << (c1 == c2) << endl;//比较 c1 和 c2 是否相等,并将结果输出到标准输出流

cout << (c1 = c2) << endl;//由于 Complex 类没有定义输出运算符的重载,这行代码会导致编译错误。

    return 0;

}

主要理解 equal_to的流程。当执行到equal_to<Complex>()(c1,c2)时,首先调用二元函数类equal_to中重载的operator==运算符函数。由于Complex 是非常规数据类型接着调用Complex中重载的operator==运算符函数完成真正的复数比较布尔值依次返回。

PS:主序中 Complex c4=plus<Complex>()(cl,2)的理解,单独用二元函数实现相应功能优势是不大的,必须与 STL 算法结合才能体现出它的好处来

int main() {

    Complex c1(1.0f, 2.0f);

    Complex c2(3.0f, 4.0f);

    Complex c3 = c1 + c2;

    Complex c4 = plus<Complex>()(c1, c2);

    Complex c;//默认构造的 Complex 对象

    vector<Complex> v;//创建一个名为 v 的 Complex 对象的向量

    v.push_back(c1);//将 c1 添加到向量 v 的末尾

    v.push_back(c2);

    v.push_back(c3);

    v.push_back(c4);

//使用 accumulate 算法对向量 v 中的元素进行累加操作,初始值为 c,使用 plus 函数对象进行元素的相加,将结果赋值给 result

    Complex result = accumulate(v.begin(), v.end(), c, plus<Complex>());

    cout << result.real << "+" << result.virt << "i";

    return 0;

}

功能是先定义4个复数,再保存至向量中,通过向量求这 4 个复数的和。STL 数值函效acumulate第4个参数表明调用的是一个系统二元函数 plus,是加法运算。在这里体现出了系统二元函数的优越性。

6.4.2 关系运算类函数对象

主要包括等于、不等于、大于、大于等于,小于、小于等于 6种运算

使用示例:

#include<functional>

#include<iostream>

using namespace std;

int main(){//以下产生一些关系运算类函数对象实体

equal_to<int>equal0bj;

not_equal_to<int>notequalobj;

greater<int>greatobj;

//运用上述对象执行功能

cout<<equal0bj(2,4)<<endl;

cout<<notequalObj(2,4)<<endl;

cout<<greatObj(2,4)<<endl;

//以下直接以临时对象执行函数功能

cout<<equal_to<int>()(2,4)<<end1;

cout<<not equal_to<int>()(2,4)<<endl;

cout<<greater<int> () (2,4)<<endl;

return 0;}

由该例可知对常规通用数据类型 char、int、flaot、string 而言可以直接按上写法行,但是对非常规数据类型,则必须重装类中的各个 operator 关系运算符。案例bool类型见上一个代码。

6.4.3逻辑运算类函数对象

主要有与、或、非三种运算

例:逻辑运算类函数对象基本用法

#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<<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;}

6.4.4函数适配器

函数适配器主要有以下几方面的主要优点:

1.可以形成复杂的有语义的表达式。例如:改变参数个数。上文讲述的算术类、关系类、逻辑运算类函数对象功能已经很强大了,但某些时候仍显不足。

例如:求某整形数组a中大于10数的个数。明显知若用系统提供的函数对象,那么一定是greater, 可是下述写法又无法体现出待比较的数10:

int n = count_if(a, a+sizeof(a)/sizeof(int), greater<int>())这种写法是错误的,利用适配器可以很好的解决这种问题。

2.可以调用类中普通的成员函数我们熟知:STL中绝大多数算法归根结底是调用功能类中重载的operator()运算符来实现的,然而,功能类中还有许多普通的成员函数,STL本身不能直接调用,必须经过适配器转换才可调用。函数适配器如下:

类型

函数

原型

说明

绑定

bind1st()

template<class Pred, class T>

binder1st<Pred> 

bind1st(const Pred& pr, const T& x);

(1)Pred是二元函数(2)二元函数对象第1个参数绑定为x

(3)返回值相于:binder1st<Pred>(pr, Pred::first_argument_type(x)).

bind2nd()

template<class Pred, class T>binder2nd<Pred> bind2nd(const Pred& pr, const T& y); 

(1)Pred是二元函数(2)元函数对象第2个参数绑定为y

(3) 返回值相当于: binder2nd<Pred>(pr, Pred::second_argument_type(y)).

取反

not1

template<class Pred>unary_negate<Pred> not1(const Pred& pr); 

(1)Pred是一元函数(2) 返回值相当于:unary_negate<Pred>(pr)

not2

template<class Pred>binary_negate<Pred> not2(const Pred& pr); 

Pred是二元函数返回值相当于binary_negate<Pred>(pr).

成员函数

适配器

mem_fun

template<class R, class T>mem_fun_t<R, T> mem_fun(R (T::*pm)()); 

调用类T中的成员函数

mem_fun_ref

template<class R, class T>mem_fun_ref_t<R, T> mem_fun_ref(R (T::*pm)()); 

调用类T中的成员函数

普通函数

适配器

ptr_fun

template<class Arg, class Result>    pointer_to_unary_function<Arg, Result>        ptr_fun(Result (*pf)(Arg));template<class Arg1, class Arg2, class Result>    pointer_to_binary_function<Arg1, Arg2, Result>        ptr_fun(Result (*pf)(Arg1, Arg2));

把全局函数进一部封装成一元函数

把全局函数进一步封装成二元函数

例:绑定取反适配器 基本用法:(次重点)

#include <functional>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

int main() {
    int a[] = {1, 3, 5, 7, 9, 8, 6, 4, 2, 0};

//计算数组 a 中小于 4 的元素个数//bind2nd 是函数对象适配器,用于将二元谓词函数
//(比如 less<int>())转化为一元谓词函数,其中第二个参数绑定为 4。
    int nCount = count_if(a, a + sizeof(a) / sizeof(int), bind2nd(less<int>(), 4));
    cout << nCount << endl; // 4

//计算数组 a 中大于 4 的元素个数bind1st把第一个参数绑定为 4。
    nCount = count_if(a, a + sizeof(a) / sizeof(int), bind1st(less<int>(), 4));
    cout << nCount << endl;// 5

//计算数组 a 中不小于 4 的元素个数,not1 是函数对象适配器对一元谓词函数取否定not p(x) bind2nd将二元谓词函数的第二个参数绑定为 4
    nCount = count_if(a, a + sizeof(a) / sizeof(int), not1(bind2nd(less<int>(), 4)));
    cout << nCount << endl;// 6

    nCount = count_if(a, a + sizeof(a) / sizeof(int), not1(bind1st(less<int>(), 4)));
    cout << nCount << endl;// 5

//将数组 a 从大到小排序。其中,greater<int>() 是一个函数对象表示按照从大到小的顺序
    sort(a, a + sizeof(a) / sizeof(int), greater<int>());

//输出流迭代器(ostream iterator)参数 cout 表示输出到标准输出流
    copy(a, a + sizeof(a) / sizeof(int), ostream_iterator<int>(cout, ""));
    cout << endl;// 9876543210

    return 0;
}

例:添加学生信息对象向量,并调用 Student 类中的 Show 函数,屏幕上显示学生信息。

下面介绍成员函数的适配器的基本用法:(次重点)

#include<algorithm>

#include<iostream>

#include<vector>

#include <string>

using namespace std;

class Student{

   string strNO;

   string strName;

public:

//构造函数的作用是在创建对象时对成员变量进行初始化。

//该构造函数接受两个参数,分别是学号和姓名,通过参数列表初始化成员变量 strNO 和 strName

   Student(string strNO,string strName):strNO(strNO),strName(strName){}

   bool show (){//成员函数 show

   cout<<strNO<<":"<<strName<<endl;

   return true;}

   };

int main()

{//针对 mem_fun_ref程序段

Student s1("1001","zhangsan");//通过构造函数初始化 strNO 和 strName 成员变量

Student s2("1002","lisi");

vector<Student>v;//创建一个名为 v 的 Student 对象的向量容器

v.push_back(s1);//将 s1 添加到向量 v 的末尾

v.push_back(s2);

//使用 for_each 算法遍历向量 v 中的每个元素,并调用每个元素的 show 成员函数进行输出

for_each(v.begin(),v.end(), mem_fun_ref(Student::show));

//创建一个名为 ps1 的指向 Student 对象的指针,并通过构造函数初始化对象

Student* ps1=new Student("1003","wangwu");

Student * ps2=new Student("1004","zhaoliu");

vector<Student*>pv;//创建一个名为 pv 的 Student 对象指针的向量容器

pv.push_back(psl);

pv.push_back(ps2);

for_each(pv.begin(), pv.end(), mem_fun(Student::show));}

注意:

1.for_each:for_each 是一个算法函数,它可以对指定范围内的元素执行指定的操作。

v.begin():v.begin() 返回一个迭代器,指向容器 v 的第一个元素。

v.end():v.end() 返回一个迭代器,指向容器 v 的尾后位置,即最后一个元素之后的位置。

mem_fun_ref:mem_fun_ref 是一个函数适配器,用于将成员函数适配为函数对象,以便在算法中调用。

Student::show:Student::show 是一个成员函数指针,指向 Student 类的 show 成员函数。

2.(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 转换后才可以应用

例: 普通函数适配器基本用法。

#include<functional>

#include<algorithm>

#include<iostream>

using namespace std;

//定义了一个名为 f 的函数,接受一个整数参数 x,并返回布尔值。函数判断 x 是否大于 3

bool f(int x){

 return x>3;

}

bool g(int x, int y){//接受两个整数参数 x 和 y

 return x>y;

}

int main()

{

int a[]={2,5,3,7,1,9,8,0,6};//初始化了一个名为a的数组元素

int nSize=sizeof(a)/sizeof(int);//计算数组 a 的大小,即元素个数

int nCount=count_if(a,a+nSize,f);//使用 count_if 算法统计数组 a 中满足条件 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;}

注意:

1.sizeof(a)返回整个数组 a 的字节数 ,sizeof(int) 返回 int 类型的字节数,这两个值相除我们可以得到数组 a 中元素的个数

2.int nCount = count_if(a, a + nSize, f);count_if 算法函数,用于统计满足条件的元素个数。a:数组 a 的起始位置,即指向第一个元素的指针。a + nSize:数组 a 的结束位置,即指向最后一个元素后一个位置的指针。f:判断条件,可以是函数、函数对象或 lambda 表达式。在这里,f 是一个函数,用于判断一个整数是否大于 。count_if 算法遍历从 a 到 a + nSize 的范围,对于范围内的每个元素:调用判断条件 f,将当前元素作为参数传递给 f,如果判断条件返回 true,则计数器加一。

3.对于三个count:

使用 count_if 算法统计数组 a 中满足条件 f 的元素个数,并将结果赋值给变量 nCount。这里使用了 ptr_fun 函数适配器,将函数 f 适配为函数对象

使用 count_if 算法统计数组 a 中满足条件 g(x, 3) 的元素个数,并将结果赋值给变量 nCount。这里使用了 bind2nd 函数适配器,将函数 g 适配为函数对象,并将 3 绑定为第二个参数

使用 count_if 算法统计数组 a 中满足条件 g(x,5) 的元素个数,并将结果赋值给变量 nCount。这里使用了 bind2nd 函数适配器,将函数 g 适配为函数对象,并将 5 绑定为第二个参数

4.第1个第2个count语中调用了普通函数g(x)。f(x)中把比条件固定了即只能求大于3的数据个数。第个count_if 中普通西用适配器 ptr_fun(f)加以饰,第1个 count_if 中直接使用做参,但它们的输出结果是同的,都是 5。所以对/参数的普通函数而言,使用 ptr_fun适配器与否并不能明显看出它的优势。

5.第 3个、第4个count_if语句中调用普通函数(二元) g(x,y)。用bind2nd(ptr_fun(g)等效于调用 g(x,3),用 bind2d(pt fun(8),5)等效调用(x,5),表明分别求大于3,大于5的数据个数,它是通过先用ptr_fun()适配修饰,再通过绑定适配器 bind2nd把g(x,y)函数的第二个参数固定为3,5。这样的好处是动志性比较强。因此这种情况下把二元普通函数用 ptr_fun 适配是必需的

6.5 两个综合例子

编程求圆和长方形的面积:

#include <functional>   // 包含函数对象相关的头文件

#include <algorithm>    // 包含算法相关的头文件

#include <iostream>     // 包含输入输出流的头文件

#include <vector>       // 包含向量容器的头文件

using namespace std;

class Shape {///在基类中定义求面积的多态虚函数

public:

    virtual bool ShowArea() = 0;    // 纯虚函数,用于展示形状的面积

};

class Circle : public Shape {//Circle 是一个继承自 Shape 的派生类,表示圆形

    float r; //半径

public:

    Circle(float r) : r(r) {}    // Circle 类的构造函数,初始化半径 r

    bool ShowArea() override {    //重载多态虚函数 ShowArea

        cout << 3.14f * r * r << endl;    // 计算圆的面积并输出

        return true;

    }

};

class Rectangle : public Shape {//Rectangle 是一个继承自 Shape 的派生类,表示矩形

    float width, height;

public:

// Rectangle 类的构造函数,初始化宽度和高度

    Rectangle(float width, float height) : width(width), height(height) {}    

    bool ShowArea() override {   // 重写虚函数 ShowArea

        cout << width * height << endl;    // 计算矩形的面积并输出

        return true;

    }

};

class AreaCollect {//收集形状对象

    vector<Shape*> v;   //是一个向量容器,用于存储 Shape 对象的指针

public:

    bool Add(Shape* pShape) {   // 向向量中添加 Shape 对象的指针的函数

        v.push_back(pShape);

        return true;

    }

    bool ShowEachArea() {   // 展示每个 Shape 对象的面积

        for_each(v.begin(), v.end(), mem_fun(&Shape::ShowArea));   // 对每个对象调用 ShowArea 函数

        return true;

    }

};

int main() {

    AreaCollect contain;   // 创建 AreaCollect 对象

    Shape* pObj1 = new Circle(10.0f);    // 创建 Circle 对象的指针pObj1,并初始化半径为 10.0

    Shape* pObj2 = new Rectangle(10.0f, 20.0f);    // 创建 Rectangle 对象的指针

    contain.Add(pObj1);   // 向 contain 添加 Circle 对象的指针

    contain.Add(pObj2);   // 向 contain 添加 Rectangle 对象的指针

    contain.ShowEachArea();   // 展示每个对象的面积

    return 0;

}

分析:

1.bool ShowEachArea() {   // 展示每个 Shape 对象的面积

        for_each(v.begin(), v.end(), mem_fun(&Shape::ShowArea));   // 对每个对象调用 ShowArea 函数

        return true;

    }

仔细解释如下:

v.begin() 返回容器 v 的起始迭代器,指向第一个元素。

v.end() 返回容器 v 的结束迭代器,指向最后一个元素的下一个位置。

for_each() 是一个算法,接受一个范围(迭代器对)和一个函数对象(或函数指针),并对范围内的每个元素执行指定的操作。

mem_fun(&Shape::ShowArea) 是一个函数适配器,用于将成员函数 Shape::ShowArea() 转换为可调用对象。它接受一个指向成员函数的指针,并返回一个可调用对象,该对象可以调用该成员函数。

for_each(v.begin(), v.end(), mem_fun(&Shape::ShowArea)) 将对容器 v 中的每个对象调用 Shape::ShowArea() 函数。

在这个代码段中,v 是一个存储了 Shape 对象指针的向量容器。for_each() 算法将遍历容器中的每个对象,并对每个对象调用 Shape::ShowArea() 函数,实现展示每个对象的面积的功能。

2.由于求两种形状的面积,因此对基本类而言最好用多态去实现,在基类 Shape 中定义了纯虚函数 bool ShowArea()。Circle(圆类)、Rectangle(矩形类)均是从 Shape 派生的,并重载了多态函数 ShowArea。

3.main 函数中向集合类中添加了一个圆对象指针,一个长方形对象指针并调用面积显示函数把面积显示出。

4.由于采用了多态思想,集合类 AreaCollect 中成员变量必须定义成 vector<Shape*>形式的,不能定义成 vector<Shape>的形式。

5.集合类AreaCollect 成员函 ShowEachArea()函数中应用了STL 算法 for_each其中的第三个参数采用了成员函数适配器 mem_fun,这是由于集合v是基于各种形状对象指针的。

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

#include <functional>

#include <algorithm>

#include <iostream>

#include <vector>

#include <iterator>

#include <string>

using namespace std;

class Student {

public:

    string strNO;

    int nGrade;

    Student(string strNO, int nGrade) : strNO(strNO), nGrade(nGrade) {}

};

//StudIndex 是一个类,继承自 binary_function<int, int, bool>,表示学生索引

class StudIndex : public binary_function<int, int, bool> {

    vector<Student>& vStud;//是一个引用,指向存储学生对象的向量容器

public:

    StudIndex(vector<Student>& vStud) : vStud(vStud) {}

    bool operator()(int a, int b) {

        return vStud[a].nGrade < vStud[b].nGrade;

    }

};

int main() {//创建了四个学生对象,并将它们添加到名为 v 的学生向量容器中

    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);

//创建了一个名为 vIndex 的整数向量容器,并将索引 0、1、2、3 添加到容器中

    vector<int> vIndex;

    vIndex.push_back(0);

    vIndex.push_back(1);

    vIndex.push_back(2);

    vIndex.push_back(3);

//使用 sort() 算法对 vIndex 进行排序,排序的依据是使用 StudIndex 对象 StudIndex(v) 进行比较

    sort(vIndex.begin(), vIndex.end(), StudIndex(v));

//使用 copy() 算法将排序后的 vIndex 中的元素复制到输出流 cout 中,以空格作为分隔符

//最后是一个输出迭代器,用于将数据输出到流 cout 中,并在每个元素之间添加空格

    copy(vIndex.begin(), vIndex.end(), ostream_iterator<int>(cout, " "));

    return 0;

}

重点代码及解释:

1.Student(string strNO, int nGrade) : strNO(strNO), nGrade(nGrade) { }

(string strNO, int nGrade) 是构造函数的参数列表,包含了学生的编号和成绩。

strNO(strNO) 是成员初始化列表的一部分,用于初始化成员变量 strNO。

第一个 strNO 是成员变量 strNO,表示学生的编号。第二个 strNO 是构造函数的参数 strNO,表示传入的学生编号。后面的成绩同理。

2.StudIndex(vector<Student>& vStud) : vStud(vStud) {}

    bool operator()(int a, int b) {  return vStud[a].nGrade < vStud[b].nGrade;}

第一句是 StudIndex 类的构造函数。

(vector<Student>& vStud) 是构造函数的参数列表,接受一个引用类型的向量容器 vStud。

vStud(vStud) 是成员初始化列表的一部分,将构造函数参数 vStud 的值赋给成员变量 vStud。

这样,在创建 StudIndex 对象时,可以传入一个向量容器,并将其赋值给 StudIndex 类的成员变量 vStud。

第二句是函数调用运算符的重载。

operator() 是函数调用运算符的重载,使得对象可以像函数一样被调用。

(int a, int b) 是函数调用运算符的参数列表,接受两个整数 a 和 b。

return vStud[a].nGrade < vStud[b].nGrade; 是函数体,根据学生对象的成绩进行比较。

vStud[a] 表示容器 vStud 中索引为 a 的学生对象。

vStud[a].nGrade 表示学生对象的成绩。

vStud[a].nGrade < vStud[b].nGrade 用于比较学生对象的成绩大小。

3.StudIndex是一个标准的二元函数类。成员变量 vector<Student> &vStud 定义了学生集合对象的引用变量,它是通过构造函数StudIndex(vector<Student>& vStud) : vStud(vStud) {}初始化。运算符函数 bool operator()(int a,int b)是真正被用的二元函数实现体。

4.main 是一个测试函数。先定义了4个学生对象,把它们依次添加到集合类v中,后索引为0开始,依次递增为1,形成这4个学生对象的索引集合 vIndex;最后通过sort函数依据成绩升序信息对索引集合 vIndex 排序,结果为“1032”,是正确的。

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

如何对vlndex排序:根据第三个参数 StudIndex(v)可知,一定是调用了 StudIndex中重载的 bool operator()(int a,int b)函数a,b 表示学生对象的索引序号,该函数体内return vStud,at(a).nGrade vStud,at(b).nGrade 返回两个学生对象成绩的比较信息StudIndex(v)的另一个含义是调用 StudIndex类的构造函数 StudIndex(vector<Studen&.vStud),把学生集合对象v传 入StudIndex 对象中

6.6 习题

1、函数适配器组合bind1nd(greater<int>(),  80)的要求是?(  )

   A. 找出小于80的元素      B. 找出大于80的元素

   C. 找出小于等于80的元素  D. 找出大于等于80的元素

2、函数适配器组合not1(bind2nd(less<int>(),  100))的要求是?(D)

//用less<int>() 表示第一个整数是否小于第二个整数。not1 函数对象进行取反操作

     A. 找出小于100的元素      B. 找出大于100的元素

     C. 找出小于等于100的元素  D. 找出大于等于100的元素

3、已知有数列{1,3,5,7,9,8,6,4,2,0},则not1(bind1st(less<int>(),4))的执行结果有几个值?

A.2个   B.3个    C.4个     D.5个       

表达式 not1(bind1st(less<int>(), 4)) 的含义是把4固定在小于符号左边此时是找大于4的,但not取反所以变成找小于等于4的值了

4、 容器vector<Student *> v,类Student下面有公有方法display(),该使用哪个系统提供的函数对象适配器方法让下面代码成立for_each(v.begin(), v.end(), _______men_fun______(&Student::display))。

mem_fun 是一个函数模板,它接受一个成员函数指针作为参数,并返回一个函数对象,该函数对象可以调用成员函数。在这种情况下,将 &Student::display 作为参数传递给 mem_fun 将返回一个函数对象,该函数对象可以调用 Student 类的 display 成员函数。

5、已知创建向量vector< Student *> v, 那么使用for_each遍历v元素, 调用Student内成员函数display()时,哪种做法是对的?A

a) men_fun封装display()转换// 适配器 mem_fun 可以将成员函数转为接受对象指针的一元函数对  

6.int main()

 {

   int a[]={1,3,5,7,9,8,6,4,2,0};

   Int nCount = count_if(a, a+sizeof(a)/sizeof(int),//数组的元素个数

   not1(bind2nd(less<int>(), 4)));//创建一个谓词函数对象,用于判断数组元素是否不小于 4

   cout << nCount << endl;

           }  

   屏幕输出的值为____6_____。

7.对下面的代码用函数对象,找出成绩范围在70 ~ 90之间的学生

struct Student {

     string sn;

     string name;

     int grade;

    };

vector<Student>v;

int n = count_if( v.begin(), v.end(),);

---------------------------------------------------------------------

struct GradeInRange {

    bool operator()(const Student& student) const {

        return (student.grade >= 70) && (student.grade <= 90);

    }       };

int n = count_if(v.begin(), v.end(), GradeInRange());

GradeInRange() 创建了一个函数对象的实例,它将在 count_if 中用于判断每个学生的成绩是否在指定范围内。

这样,n 就是成绩范围在 70 ~ 90 之间的学生数量

8.普通函数适配器基本用法。

#include< functional>  #include<algorithm>  #include<iostream>

bool f(int x)   {  return x>3;  }

bool g(int x, int y)  {  return x>y;  }

int main(){

  int a[]={2,5,3,7,19,8,0,6};

  int nsize=sizeof (a)/sizeof (int)

  int nCount= count_if(a, a+nsize, f);    cout<<nCount<<endl;//5

  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;   }

9、函数适配器的使用

   mem_fun 适用与指针  mem_fun_ref适用于实际对象   ptr_fun不是发生器

10、count_if,count; find, find_if,有无if有何区别?如何使用?

自定义处理需要带谓词。

find(): 在单迭代器序列中找到某个值 第一次出现的位置

find_if(): 在单迭代器序列中找出符合谓词的第一个元素

find 算法用于在指定范围内查找指定值的元素,并返回指向该元素的迭代器。使用 find 算法时,需要提供要查找的值,它会线性地在指定范围内进行查找,直到找到匹配的元素或者查找到结尾位置。例如,find(v.begin(), v.end(), value) 可以在容器 v 中查找值为 value 的元素,并返回一个指向该元素的迭代器。如果未找到匹配的元素,则返回 v.end()。

find_if 算法用于在指定范围内查找满足特定条件的元素,并返回指向该元素的迭代器。使用 find_if 算法时,需要提供一个谓词(即返回 bool 类型的函数对象或函数),它将应用于容器中的每个元素,直到找到使谓词返回 true 的元素或者查找到结尾位置。如find_if(v.begin(), v.end(),predicate) 可以在容器 v 中查找满足谓predicate 的元素,并返回一个指向该元素的迭代器。如果未找到满足条件的元素,则返回 v.end()。

count(): 在序列中统计某个值出现的次数

count_if(): 在序列中统计与某谓词(表达式)匹配的次数

11.返回布尔值的函数对象称为_谓词_

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值