C++ STL 函数对象的基本使用 一元谓词和二元谓词 find_if和sort源码解析
创始人
2025-05-28 16:20:03

STL- 函数对象

文章目录

  • STL- 函数对象
    • 1 函数对象
      • 1.1 函数对象概念
      • 1.2 函数对象使用
    • 2. 谓词
      • 2.1 谓词概念
      • 2.2 一元谓词
      • 2.3 二元谓词

1 函数对象

1.1 函数对象概念

概念:

  • 重载函数调用操作符的类,其对象常称为函数对象
  • 函数对象使用重载的()时,行为类似函数调用,也叫仿函数

本质: 函数对象(仿函数)是一个,不是一个函数

1.2 函数对象使用

特点:

  • 函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
  • 函数对象超出普通函数的概念,函数对象可以有自己的状态。因为函数对象本质是一个类,类中的成员属性可以记录状态
  • 函数对象可以作为参数传递

代码示例:

//1、函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
class MyAdd
{
public:int operator()(int v1, int v2)//有参数,有返回值{return v1 + v2;}
};void test1()
{MyAdd myadd;//创建函数对象cout << "函数对象特点1:类似普通函数的调用,有参数、有返回值" << endl;cout << "myadd = " << myadd(10, 20) << endl;cout << string(50, '-') << endl;
}//2、函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint
{
public:MyPrint(){this->count = 0;//初始化为0}void operator()(string test){cout << test << endl;this->count++;//调用一次就记录一次}int count;//内部自己状态
};void test2()
{MyPrint myprint;//创建函数对象cout << "函数对象特点2:函数对象可以有自己的状态" << endl;myprint("Keep working out!");//函数对象调用myprint("Keep fitting!");myprint("Keep slenderizing!");cout << "myprint调用次数为:" << myprint.count << endl;cout << string(50, '-') << endl;
}//3、函数对象可以作为参数传递
void doPrint(MyPrint& myprint, string test)
{myprint(test);
}void test3()
{MyPrint myprint;//创建函数对象cout << "函数对象特点3:函数对象作为参数传递" << endl;doPrint(myprint, "Bella要减肥");//函数对象作为参数传递cout << string(50, '-') << endl;
}

在这里插入图片描述

函数对象的特点1,其实在之前的学习笔记中有使用过,比如在list容器、set容器和map容器这几个容器的自定义数据类型排序以及内置数据类型的降序排序都使用过。
对于特点2,普通函数没有函数对象这样可以有成员属性来记录本身的状态,比如统计函数的调用次数,因为函数对象本质是一个类,类是可以有成员属性的。
对于特点3,函数对象可以做为一个参数传递。

总结: 仿函数写法非常灵活,可以作为参数进行传递。

2. 谓词

2.1 谓词概念

概念:

  • 返回bool类型仿函数称为谓词
  • 如果operator()接受一个参数,即形参列表中有一个参数,叫一元谓词
  • 如果operator()接受两个参数,即形参列表中有两个参数,叫二元谓词

2.2 一元谓词

代码示例:

class LargerthanSix
{
public:bool operator()(int val)//布尔类型的仿函数{return val > 6;}
};void test1()
{vector v;for (int i = 0; i < 10; i++){v.push_back(1 + i);}//在容器中找是否有大于6的元素可以用find_if//LargerthanSix() 创建匿名的函数对象(仿函数)vector::iterator it = find_if(v.begin(), v.end(), LargerthanSix());//输出全部大于6的结果for (; it != v.end(); it++){cout << "比6大的数有:" << *it << endl;if (it == v.end()){cout << "没有比6大的数!" << endl;break;}}
}

在这里插入图片描述

注意点:
首先,find_if函数的第一个和第二个参数是迭代器,表示一个区间中查找;第三个参数是仿函数,如果看到pred表示需要传入一个仿函数,下图示
在这里插入图片描述
其次,选中find_if→右键→转到定义 查看源码,find_if的返回值是一个迭代器
在这里插入图片描述
以上是find_if源码,说明如下

  1. find_if的参数列表:_First和 _Last都是迭代器,表示查找的区间,_Pred是仿函数,其中 _Last还使用了const来修饰。
  2. find_if的返回值:最后return的部分,返回的还是_First,说明最后返回的是一个迭代器。旁边的注释还说明了,find_if返回第一个满足仿函数条件的迭代器。
  3. find_if的for循环就是find_if的实现部分:_UFirst逐步遍历容器,循环结束的条件就是起始迭代器不等于结束迭代器。
  4. 通过if语句来实现满足条件值的查找:_Pred利用重载的()调用代码写的仿函数,通过星号* 解引用使得迭代器访问容器的数据,然后传给仿函数判断当前数据是否满足条件。具体来说,*_UFirst这个数据传给仿函数_Pred,如果返回真,说明找到满足条件的值,break结束循环,最后返回。如果返回假,_UFirst移动,直到_UFirst等于结束迭代器_ULast。

总结: 参数只有一个的谓词,称为一元谓词

2.3 二元谓词

示例:

class Mysort
{
public:bool operator()(int v1, int v2)//布尔类型的仿函数{return v1 > v2;}
};void test1()
{vector v;v.push_back(30);v.push_back(18);v.push_back(28);v.push_back(23);v.push_back(25);v.push_back(26);cout << "标准算法sort排序 升序\nv:  ";sort(v.begin(), v.end());for(vector::iterator it=v.begin();it!=v.end();it++){cout << *it << "  ";}cout << endl << string(40, '-') << endl;//使用函数对象 改变算法排序规则 降序cout << "\n制定算法sort排序 降序\nv:  ";sort(v.begin(), v.end(), Mysort());//Mysort() 匿名对象for (vector::iterator it = v.begin(); it != v.end(); it++){cout << *it << "  ";}cout << endl << string(40, '-') << endl;
}

在这里插入图片描述
这个sort算法当中需要的是一个谓词,因此仿函数是bool类型。
在这里插入图片描述

总结: 参数只有两个的谓词,称为二元谓词

相关内容

热门资讯

“我的名字在徽章上!” 这场生... “这是我的名字!”12月23日,在“星点心愿”饺子馆,当福建海峡银行青年志愿者代表递上印有特别图案的...
闪评 | 以压促变vs强势反击... 当地时间12月23日,联合国安理会就委内瑞拉局势举行紧急会议。委内瑞拉常驻联合国代表在会上谴责美国违...
最新或2023(历届)南科大在... 南科大明年在皖招生65人 看重创新潜质、综合素质   日前,南科大正式发布最新或2023(历届)自主...
今年甘肃高考难吗,最新或202... 今年甘肃高考难吗,最新或2023(历届)甘肃高考难度分析难易程度解析语文:新增古代文化常识题  点评...
今年贵州高考难吗,最新或202... 今年贵州高考难吗,最新或2023(历届)贵州高考难度分析难易程度解析 在贵州师范大学的高考评卷基地内...