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.返回布尔值的函数对象称为_谓词_。