【C++】STL 算法 ① ( STL 算法相关头文件 | 函数对象 / 仿函数 简介 | 函数调用操作符 | 重写函数调用操作符的类 | 函数对象 与 普通函数区别 )





一、STL 算法相关头文件



标准模板库 STL 算法 都定义在 <algorithm> , <numeric> <functional> 三个头文件中 ;

使用 STL 标准模板库 算法时 , 导入上述 3 个头文件 , 导入时根据需求导入即可 , 不必都导入 ;

#include <algorithm>
#include <numeric>
#include <functional>
  • <algorithm> 头文件 是 3 个 STL 算法头文件中 包含算法最多的一个 , 包含常用的 : 比较算法、交换算法、查找算法、遍历算法、复制算法、修改算法、反转算法、排序算法、合并算法 等 算法 ;
  • <numeric> 头文件 包含的算法较少 , 这些算法 主要是 在 序列 上面进行简单数学运算 的模板函数 , 如 : 在 序列 上 执行 加减乘除 操作 ;
  • <functional> 头文件 中 只 定义了一些模板类 , 这些模板类的作用是 声明函数对象 ;

STL 标准模板库 提供了 实现算法 的 模板函数 , 借助这些 模板函数 , 只需要几行代码 , 即可实现相应算法的复杂功能 , 极大地提升了开发效率 和 程序的可维护性 ;





二、函数对象 / 仿函数 简介



1、函数对象 / 仿函数 博客回顾


在之前的博客 【C++】STL 容器 - set 集合容器 ⑤ ( 仿函数 functor 简介 | 仿函数 functor 调用 | 自定义类排序规则 - 仿函数 / 重载 < 运算符函数 ) 中 简单的介绍了 " 仿函数 " 概念 ;

在结构体中的 operator() 就是 重载 函数调用操作符 () 函数 ; 在 C++ 语言中 struct 结构体 与 class 类是等同的 , struct 结构体中的成员就是类成员 ;

struct IntCompare {
    
    
	bool operator()(const int& a, const int& b) const volatile {
    
    
		return (a < b);	// 降序排序  
	}
};

创建 仿函数类 的 函数对象 , 然后通过 函数对象 调用 仿函数类 中的 " 重载 函数调用操作符 () 函数 " ;

	// 创建 仿函数对象
	IntCompare ic;
	// 通过 仿函数对象 调用仿函数
	bool b = ic(1, 2);

2、函数调用操作符


" 函数调用操作符 " 是一种用于 调用函数的符号 , 通常是一对圆括号 () ;

在 C++ 语言中 , 函数调用操作符都扮演着重要的角色 ;

" 函数调用操作符 " 的 主要作用是 将 函数 与其 参数 联系起来 , 并执行函数的代码 ;


函数调用操作 :

  • 首先 , 在调用函数之前 , 必须 先 定义函数 并 指定其 参数列表 ;
  • 然后 , 使用 函数调用操作符 告诉编译器将这些参数传递给函数 , 并执行函数的代码 ;
  • 最后 , 函数执行后 , 将函数的返回值可以被赋值给变量 , 或者 直接使用 ;

定义一个函数 fun :

void fun(){
    
    }

调用 fun 函数 , 其中 函数名 " fun " 后面的 括号 " () " 就是 函数调用操作符 ;

fun();

3、函数对象 / 仿函数 - 重写函数调用操作符的类


" 函数对象 " 是 重载 函数调用操作符 " () " 的 类 , 又称为 " 仿函数 " , 它们是 行为类似函数 的 对象 ;

" 函数对象 " 本质是 类对象 , 其表现出一个函数的特征 , 通过该对象可以调用函数 , 通过对象名(参数列表) 的方式 可以调用 重载 () 运算符函数 , 如果没有 创建该 函数对象的 上下文 代码 , 完全可以 把 函数对象 看作一个普通函数 ;


函数对象 / 仿函数 可以看做 Java 中的接口类 , 匿名内部类 , Kotlin 中的 Lambda 表达式 , 可以作为 回调函数 使用 ;


代码示例 :

#include "iostream"
using namespace std;
#include <algorithm>
#include "functional"

//函数对象 类重载了()
template <typename T>
class PrintT{
    
    
public:
	void operator()(T& t){
    
    
		cout << t << endl;
	}
};

int main() {
    
    

	int a = 666;

	// 创建函数对象
	PrintT<int> obj;

	// 通过函数方式 调用 函数对象 
	obj(a);


	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
};

执行结果 :
在这里插入图片描述


4、函数对象 与 普通函数区别


函数对象 其 本质 是 类对象 , 能突破 函数的 概念 , 该类对象中 有成员变量 , 可以存储临时状态数据 ;


函数对象 / 仿函数 : 英文名称 " Function Objects / Functors " ;

  • 定义 : 函数对象 是 重载了 operator() 的类的对象 , 也可以是结构体 ; 这种 类 / 结构体 的 对象 可以像函数一样被调用 ;
  • 状态保持 : 函数对象可以有自己的数据成员,因此它们可以保持状态。这意味着在连续调用中,它们可以记住之前调用的信息。
  • 灵活性 : 由于 函数对象 是 类的实例对象 , 开发时可以 通过 继承 和 重载 来 修改 重载函数调用操作符函数 的行为 ;
  • 效率 : 在某些情况下 , 使用函数对象可能 比使用普通函数更有效率 , 因为 编译器可以进行内联优化 ;

普通函数 : 英文名称 " Ordinary Functions " ;

  • 定义 : 普通函数 是 C++ 言 中的 重要组件 , 通过函数名后跟括号和参数列表来调用 ;
  • 状态保持 : 普通函数 不保持状态 , 每次调用结束后 , 其内部的局部变量的声明周期都会结束 , 并自动回收栈内存 ;
  • 灵活性 : 普通函数可以通过参数传递来定制行为 , 但 不能通过继承和重载来改变其行为 ;
  • 效率 : 普通函数通常 没有额外的函数调用开销 , 但在某些情况下 , 编译器可能无法对它们进行内联优化 ;

5、函数对象 与 普通函数 使用场景


函数对象 与 普通函数 使用场景 :

  • 函数对象 : 如果 调用 多个函数 , 每个函数需要保持各自的状态 , 此时需要使用 函数对象 ;
  • 普通函数 : 如果只需要 执行 一个简单的 , 无状态的 , 一次性的 函数调用 操作 , 此时推荐使用 普通函数 ;

猜你喜欢

转载自blog.csdn.net/han1202012/article/details/135377145