C++ STL 教程
希望您已经理解了我们之前讨论过的 C++ 模板的概念。C++ STL(标准模板库)是一组功能强大的 C++ 模板类,它为通用类和函数提供了模板,这些模板实现了许多流行且常用的算法和数据结构,例如向量、列表、队列和堆栈。
C++ 标准模板库的核心是以下三个结构良好的组件 -
Sr.No | 组件和描述 |
---|---|
1 | 容器 容器用于管理特定类型对象的集合。容器有多种类型,例如双端队列 (deque)、列表 (list)、向量 (vector)、映射 (map) 等。 |
2 | 算法 算法作用于容器。它们提供对容器内容进行初始化、排序、搜索和转换的方法。 |
3 | 迭代器 迭代器用于遍历对象集合的元素。这些集合可以是容器,也可以是容器的子集。 |
下一章讨论 C++ 标准库 (C++ STL) 时,我们将讨论所有三个 C++ STL 组件。现在,请记住,这三个组件都拥有丰富的预定义函数,可以帮助我们以非常轻松的方式完成复杂的任务。
示例
让我们以以下程序为例,该程序演示了向量容器(C++ 标准模板),它类似于数组,但有一个例外:当数组增长时,它会自动处理自身的存储需求 -
#include <iostream> #include <vector> using namespace std; int main() { // 创建一个向量来存储 int vector<int> vec; int i; // 显示 vec 的原始大小 cout << "vector size = " << vec.size() << endl; // 将 5 个值推送到向量中 for(i = 0; i < 5; i++) { vec.push_back(i); } // 显示 vec 的扩展大小 cout << "extended vector size = " << vec.size() << endl; // 从向量中访问 5 个值 for(i = 0; i < 5; i++) { cout << "value of vec [" << i << "] = " << vec[i] << endl; } // 使用迭代器访问值 vector<int>::iterator v = vec.begin(); while( v != vec.end()) { cout << "value of v = " << *v << endl; v++; } return 0; }
当编译并执行上述代码时,它会产生以下结果 -
vector size = 0 extended vector size = 5 value of vec [0] = 0 value of vec [1] = 1 value of vec [2] = 2 value of vec [3] = 3 value of vec [4] = 4 value of v = 0 value of v = 1 value of v = 2 value of v = 3 value of v = 4
以下是与上例中使用的各种函数相关的注意事项 -
- push_back() 成员函数在向量末尾插入值,并根据需要扩展其大小。
- size() 函数显示向量的大小。
- begin() 函数返回指向向量开头的迭代器。
- end() 函数返回指向向量末尾的迭代器。
C++ 标准模板库组件
C++ 标准模板库的核心是以下四个结构良好的组件 -
- 容器
- 算法
- 迭代器
- 函子(函数对象)
容器
容器是一种用于存储和管理数据或对象集合(例如向量、列表、集合和映射)的数据结构。这里我们将介绍几种类型的容器。
1. 序列容器
它们是按特定线性顺序存储元素的容器。它们允许直接访问元素,并具有管理元素顺序和排列的功能。
- 数组 − 这些是固定大小的元素集合。
- 向量 − 它是一个动态数组,可以根据需要调整其大小。
- 双端队列 − 双端队列,允许在两端快速插入和删除。
- 列表 − 双向链接列表,允许双向迭代。
- 前向列表 − 它是一个单链表,允许高效的插入和删除操作,但只能单向遍历。
- 字符串 − 在 C++ 中,字符串也被视为顺序容器,它被实现为一个动态字符数组,其行为与其他顺序容器类似。
2. 关联容器
它按排序顺序存储元素,从而允许基于键快速检索。这些容器采用键值对结构,其中每个元素都与一个唯一的键相关联。此键用于快速访问相应的值。
- Set − 它是按特定顺序排序的唯一元素的集合。
- Map − 它是键值对的集合,其中键是唯一的。
- Multiset − 它类似于集合,但允许重复元素。
- Multimap − 它类似于地图,但允许重复键。
3.无序关联容器
它以无序的方式存储元素,从而允许基于键进行高效的访问、插入和删除。它们不维护元素的排序顺序,而是使用散列来组织数据。
- unordered_set − 它是唯一元素的集合,没有任何特定顺序。
- unordered_map − 它是键值对的集合,没有特定顺序。
- unordered_multiset − 它允许重复元素,而没有特定顺序。
- unordered_multimap − 它允许重复键。没有特定顺序。
4. 容器适配器
它为现有容器提供了不同的接口。
算法
C++ 标准模板库 (STL) 中的算法是一个大型函数集合,专门用于对容器执行操作。这些算法使用迭代器实现,迭代器用于遍历容器,而无需了解其内部结构。
非修改序列算法
- for_each − 它应用于函数中,以找出范围内的每个元素。
- count − 它计算范围内某个值出现的次数。
- find() − 它查找某个值的第一次出现在一个范围内。
- find_if − 查找第一个满足谓词的元素。
- find_if_not − 查找第一个不满足谓词的元素。
- equal − 检查两个范围是否相等。
- search − 在序列中搜索子序列。
1.修改序列算法
- copy() − 将元素从一个范围复制到另一个范围。
- copy_if − 将满足谓词的元素复制到另一个范围。
- copy_n − 将特定数量的元素从一个范围复制到另一个范围。
- move − 它将元素从一个范围移动到另一个范围。
- transform() − 将函数应用于范围并存储结果。
- remove − 从范围中删除具有特定值的元素。
- remove_if − 删除满足谓词的元素。
- unique − 删除连续重复的元素。
- reverse() − 反转范围中元素的顺序。
- swap() − 交换元素。
2.排序算法
- sort − 对一定范围内的元素进行排序。
- stable_sort − 对元素进行排序,同时保持等效元素的相对顺序。
- partial_sort − 对一定范围内的部分元素进行排序。
- nth_element − 对范围进行划分,使第 n 个元素位于其最终位置。
3.搜索算法
- binary_search − 检查元素是否存在于已排序的范围内。
- lower_bound − 搜索可以插入元素以保持顺序的第一个位置。
- upper_bound − 搜索大于指定值的第一个元素的位置。
- binary_search − 检查元素是否存在于已排序的范围内。
- lower_bound − 查找可以插入元素以保持顺序的第一个位置。
- upper_bound − 搜索大于指定值的第一个元素的位置。
- equal_range − 返回相等元素的范围。
4. 堆算法
- make_heap − 根据一定范围创建堆。
- push_heap − 向堆中添加元素。
- pop_heap − 从堆中删除最大元素。
- sort_heap − 对堆中的元素进行排序。
5.集合算法
- set_union − 计算两个集合的并集。
- set_intersection − 计算两个集合的交集。
- set_difference − 计算两个集合的差集。
- set_symmetric_difference − 计算两个集合的对称差集。
6.数值算法
- accumulate − 计算一个范围的和(或其他运算)。
- inner_product − 计算两个范围的内积。
- adjacent_difference − 计算相邻元素之间的差值。
- Partial_sum − 计算一个范围的部分和。
7.其他算法
- generate − 使用函数生成的值填充指定范围。
- shuffle − 随机打乱指定范围内的元素。
- partition − 根据谓词将元素分成两组。
迭代器
C++ 标准模板库 (STL) 中的迭代器是指向容器内元素的指针,容器提供统一的数据访问和操作接口。它们充当算法和容器之间的桥梁。
- 输入迭代器 − 它们允许对元素进行只读访问。
- 输出迭代器 − 它们允许对元素进行只写访问。
- 前向迭代器 − 它们允许读写,并且可以递增。
- 双向迭代器 − 它们可以递增和递减。
- 随机访问迭代器 − 它们支持算术运算,并且可以访问元素。
函数对象(函子)
C++ 中的函子(或称函数对象)是一种可以像函数一样调用的对象。它可以像常规函数一样被调用。此功能通过重载函数调用运算符 ()实现。
本文将根据函子执行的操作,探索不同类型的函子。
1. 算术函子
在 C++ 中,算术运算符用于执行基本的数学运算。以下是常见的算术运算符及其示例。
- 加法 (+) − 它将两个值相加得出它们的和。
- 减法 (-) − 计算两个值之间的差。
- 乘法 (*) − 它将两个值相乘得出它们的乘积。
- 除法 (/) − 它将一个值除以另一个值,得出商。
- 模数 (%) − 返回一个值除以另一个值后的余数。
- 取反 () − 返回参数的取反值。
2.比较函子
比较函子用于比较值,尤其是在容器中排序或搜索时。
- 小于 (<) − 如果第一个值小于第二个值,则比较并返回 true。
- 大于 (>) − 如果第一个值大于第二个值,则比较并返回 true。
- 小于或等于 (≤) − 如果第一个值小于或等于第二个值,则比较并返回 true。
- 大于或等于 (≥) − 如果第一个值大于或等于第二个值,则比较并返回 true。
- 等于 (=) − 如果相等,则比较并返回 true。
- 不等于(≠) − 比较两个布尔值,如果不相等则返回 true。
3. 逻辑函子
逻辑函子执行逻辑运算,在涉及布尔逻辑的场景中非常有用。
- 逻辑与函子 (&&) − 如果两个布尔参数中至少有一个或两个为 false,则返回 false;否则返回 true。
- 逻辑或函子 (||) − 如果两个布尔参数中至少有一个或两个为 true,则返回 true。
- 逻辑非函子 (!) − 如果布尔参数为 false,则返回 true;反之,返回与提供的布尔值相反的值。
4.按位函数
- bit_and − 对两个操作数执行按位"与"运算,
- bit_or − 对两个操作数执行按位"或"运算,
- Bit_xor − 对两个操作数执行按位"异或"运算,并返回相应的输出。
实用程序
实用程序是指一组通用组件,它们提供编程的基本功能,并允许对数据和类型进行高效的操作。
1. 对和元组
- 对 − 它是一个容器,可以容纳两个可能不同类型的值,分别作为第一和第二个成员。
- 元组 − 一个固定大小的集合,可以存储多个不同类型的值,并通过 std::get 访问每个元素。
2.智能指针
- unique_ptr − 它是一个拥有动态分配对象的智能指针,当指针超出范围时会自动释放该对象。
- Shared_ptr − 它是一个智能指针,允许多个指针通过使用引用计数来管理生命周期,从而共享动态分配对象的所有权。
- Weak_ptr − 它是一个智能指针,它提供对由 std::shared_ptr 管理的对象的非拥有引用,从而防止循环引用。
3.可选、变体和任意类型 (C++17)
- Optional − 它是一个包装器,表示可选值,指示值是否存在。
- variant − 它是一个类型安全的联合体,可以保存多个指定类型之一,从而提供一种处理多种类型而不会产生歧义的方法。
- any − 它是一个类型安全的容器,用于存储任意类型的值,允许动态类型擦除和运行时类型检查。
4.内存管理和数值限制
- allocator − 用于管理动态内存。
- numeric_limits − 提供有关基本数据类型属性的信息。
5.类型特征
这是一组模板,允许您执行编译时类型检查。
- is_same − 它是一个类型特征,用于检查两个类型是否相同,如果相同则返回 true,否则返回 false。
- is_integral − 它是一个类型特征,用于确定给定类型是否为整数类型(例如,int、char、long),对于整数类型返回 true,对于其他类型返回 false。
- is_floating_point − 它是一个类型特征,用于检查给定类型是否为浮点类型(例如,float、double、long double),对于浮点类型返回 true,对于非浮点类型返回 false。
- enable_if − 它是一个实用程序,用于根据编译时条件,通常用于 SFINAE(替换失败不是错误)。
STL 基本功能:简单示例
这是一个演示 STL 基本功能的简单示例
#include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> vec = {5, 2, 8, 1, 3}; // 对向量进行排序 std::sort(vec.begin(), vec.end()); // 显示已排序的元素 std::cout << "Sorted vector: "; for (int num : vec) { std::cout << num << " "; } std::cout << std::endl; // 查找元素 auto it = std::find(vec.begin(), vec.end(), 3); if (it != vec.end()) { std::cout << "Element 3 found at position: " << std::distance(vec.begin(), it) << std::endl; } else { std::cout << "Element 3 not found" << std::endl; } return 0; }
输出
Sorted vector: 1 2 3 5 8 Element 3 found at position: 2
说明
此代码提供了 STL 在各个地方的使用
- std::vector<int> vec − 上述程序使用了 std::vector,这是一个允许动态数组管理的 STL 容器。
- std::sort(vec.begin(), vec.end()) − 此函数是操作容器的 STL 算法的一部分,它按升序对向量的元素进行排序。
- std::find(vec.begin(), vec.end(), 3) − 此函数在向量中搜索值 3,如果找到则返回一个迭代器。
- begin() 和 vec.end() − 这些函数分别返回指向向量开头和结尾的迭代器。
- std::distance(vec.begin(), it) − 此函数计算两个迭代器之间的元素数量(在本例中,从向量的开头开始)向量到找到的迭代器),实际上给出了找到元素的索引。
使用 STL 的好处
在 C++ 中使用标准模板库 (STL) 有很多好处,因为它提供了大量预定义的数据结构和算法,从而节省时间和精力,并减少了从头实现这些结构和算法的需求。它还促进了代码重用、模块化、类型安全和灵活性。这使得相同的代码可以处理不同的数据类型。总的来说,它提高了编程效率和代码质量。