1. 容器 (Containers)
容器是用来存放数据的集合。它们可以分为三类:序列式容器、关联式容器和无序关联式容器。
A. 序列式容器 (Sequence Containers)
元素按线性顺序排列。
容器 | 头文件 | 底层实现 | 主要特性和适用场景 |
---|---|---|---|
std::vector | <vector> | 动态数组 | 最常用。随机访问快 (O(1))。尾部插入/删除快 (摊销O(1))。中间插入/删除慢 (O(n))。 |
std::deque | <deque> | 双端队列 | 随机访问快 (O(1))。头尾插入/删除快 (摊销O(1))。中间插入/删除慢 (O(n))。 |
std::list | <list> | 双向链表 | 随机访问慢 (O(n))。任意位置插入/删除快 (O(1))。不支持 [] 和 at() 。 |
std::forward_list | <forward_list> | 单向链表 | 比 list 更节省空间,但只能单向遍历。 |
std::array | <array> | 固定大小数组 | C++11引入。大小在编译时确定,性能与原生数组相当。不支持动态扩容。 |
std::string | <string> | vector<char> 的特化 | 专用于处理字符串,接口丰富。 |
通用API (大部分容器共有):
- 构造函数:
Container()
,Container(size_type n)
,Container(n, const value_type& val)
,Container(InputIt first, InputIt last)
,Container(const Container& other)
,Container(std: :initializer_list<T>)
- 大小/容量:
empty()
: 判断是否为空。size()
: 返回元素数量。max_size()
: 返回可容纳的最大元素数量。
- 迭代器:
begin()
/cbegin()
: 返回指向第一个元素的迭代器。end()
/cend()
: 返回指向末尾之后位置的迭代器。rbegin()
/crbegin()
: 返回指向反向序列第一个元素的迭代器。rend()
/crend()
: 返回指向反向序列末尾之后位置的迭代器。
- 修改器:
clear()
: 清空所有元素。insert()
: 在指定位置插入元素。erase()
: 删除指定位置或范围的元素。swap()
: 与另一个同类型容器交换内容。
序列式容器特有API:
- 元素访问:
front()
: 访问第一个元素。back()
: 访问最后一个元素。operator[]
: 访问指定索引的元素(不检查越界,仅vector
,deque
,array
,string
)。at()
: 访问指定索引的元素(检查越界,会抛出std::out_of_range
异常)。
- 修改器:
push_back()
: 在末尾添加元素 (vector
,deque
,list
,string
)。pop_back()
: 移除末尾元素 (vector
,deque
,list
,string
)。push_front()
: 在开头添加元素 (deque
,list
,forward_list
)。pop_front()
: 移除开头元素 (deque
,list
,forward_list
)。resize()
: 改变容器大小。emplace()
/emplace_back()
/emplace_front()
: C++11引入,在指定位置就地构造元素,避免拷贝。
B. 关联式容器 (Associative Containers)
元素根据键值自动排序,底层通常是红黑树。
容器 | 头文件 | 键值对 | 是否允许重复键 | 主要特性 |
---|---|---|---|---|
std::set | <set> | 否 (值即是键) | 否 | 存储唯一的、已排序的元素。 |
std::multiset | <set> | 否 (值即是键) | 是 | 存储可重复的、已排序的元素。 |
std::map | <map> | 是 (pair<const Key, T> ) | 否 | 存储唯一的键值对,根据键排序。 |
std::multimap | <map> | 是 (pair<const Key, T> ) | 是 | 存储可重复的键值对,根据键排序。 |
通用API:
- 查找操作 (效率高,O(log n)):
find(const Key& key)
: 查找键,返回迭代器或end()
。count(const Key& key)
: 返回键匹配的元素数量(set
/map
中为0或1)。lower_bound(const Key& key)
: 返回第一个不小于key
的元素的迭代器。upper_bound(const Key& key)
: 返回第一个大于key
的元素的迭代器。equal_range(const Key& key)
: 返回一个pair
,包含上述两个bound
的结果。
- 修改器:
insert(value)
: 插入元素/键值对。返回pair<iterator, bool>
(map
/set
)或iterator
(multimap
/multiset
)。erase(key)
/erase(iterator)
: 删除元素。emplace()
: C++11引入,就地构造元素。
map
特有API:operator[]
: 如果键存在,返回其值的引用;如果不存在,则插入一个新元素并返回其值的引用。at(const Key& key)
: C++11引入,访问键对应的值,若键不存在则抛出异常。
C. 无序关联式容器 (Unordered Associative Containers)
C++11引入,元素无序,基于哈希表实现。
容器 | 头文件 | 键值对 | 是否允许重复键 | 主要特性 |
---|---|---|---|---|
std::unordered_set | <unordered_set> | 否 | 否 | 快速查找、插入、删除(平均O(1)),不排序。 |
std::unordered_multiset | <unordered_set> | 否 | 是 | 同上,但允许重复。 |
std::unordered_map | <unordered_map> | 是 | 否 | 同上,存储键值对。 |
std::unordered_multimap | <unordered_map> | 是 | 是 | 同上,允许重复键。 |
API与关联式容器类似,但查找、插入、删除的平均时间复杂度为O(1),最坏为O(n)。没有lower_bound
/upper_bound
等依赖排序的API。
D. 容器适配器 (Container Adapters)
它们不是独立的容器,而是对现有序列式容器(默认是std::deque
)的接口进行封装,提供特定的行为模式。
适配器 | 头文件 | 行为模式 | 默认底层容器 | 主要API |
---|---|---|---|---|
std::stack | <stack> | LIFO (后进先出) | std::deque | push() , pop() , top() , empty() , size() |
std::queue | <queue> | FIFO (先进先出) | std::deque | push() , pop() , front() , back() , empty() , size() |
std::priority_queue | <queue> | 优先队列 | std::vector | push() , pop() (移除最大/顶元素), top() (访问最大/顶元素), empty() , size() |
2. 迭代器 (Iterators)
迭代器是泛型指针,提供对容器中元素的访问。根据能力分为五类:
- 输入迭代器 (Input Iterator):
*it
(读),it++
(单次向前)。 - 输出迭代器 (Output Iterator):
*it = val
(写),it++
(单次向前)。 - 前向迭代器 (Forward Iterator):
*it
(读/写),it++
(多次向前)。 - 双向迭代器 (Bidirectional Iterator):
*it
(读/写),it++
,it--
(双向移动)。 - 随机访问迭代器 (Random Access Iterator):
*it
(读/写),it++
,it--
,it + n
,it - n
,it[n]
,it1 - it2
。
迭代器相关辅助函数 (C++11+):
std::begin(container)
/std::end(container)
: 获取容器的 begin/end 迭代器,比成员函数更通用。std::next(it, n=1)
: 返回it
向前移动n
步后的迭代器。std::prev(it, n=1)
: 返回it
向后移动n
步后的迭代器。std::distance(first, last)
: 计算两个迭代器之间的距离。std::advance(it, n)
: 将迭代器it
移动n
步。
3. 算法 (Algorithms)
定义在 <algorithm>
和 <numeric>
头文件中,对迭代器指定的范围进行操作。
A. 非修改性序列操作
for_each(first, last, f)
: 对范围内每个元素执行f
。find(first, last, val)
: 查找val
。find_if(first, last, pred)
: 查找满足pred
条件的元素。count(first, last, val)
: 统计val
出现的次数。count_if(first, last, pred)
: 统计满足pred
条件的元素数量。equal(first1, last1, first2)
: 比较两个序列是否相等。search(first1, last1, first2, last2)
: 在序列1中查找序列2。all_of/any_of/none_of(first, last, pred)
: C++11, 判断是否所有/任意/没有元素满足pred
。
B. 修改性序列操作
copy(first, last, result_first)
: 复制序列。move(first, last, result_first)
: C++11, 移动序列。transform(first, last, result_first, op)
: 对序列每个元素应用op
后存入result
。replace(first, last, old_val, new_val)
: 替换所有old_val
。fill(first, last, val)
: 用val
填充序列。generate(first, last, gen)
: 用生成器gen
的结果填充序列。remove(first, last, val)
: “移除”所有val
(实际是把不等于val
的元素前移,返回新的逻辑终点)。常与container.erase()
配合使用。unique(first, last)
: “移除”连续的重复元素,返回新的逻辑终点。reverse(first, last)
: 反转序列。rotate(first, middle, last)
: 旋转序列,使middle
成为新的第一个元素。random_shuffle
/shuffle(first, last, g)
: 随机打乱序列(shuffle
是C++11推荐版本)。
C. 排序和搜索操作
(通常要求随机访问迭代器,部分也适用于双向迭代器)
sort(first, last, comp)
: 对序列排序(comp
是可选的比较函数)。stable_sort(first, last, comp)
: 稳定排序(保持相等元素的相对顺序)。partial_sort(first, middle, last)
: 将前N个最小元素排序并放在序列开头。is_sorted(first, last)
: 检查序列是否有序。- 二分查找 (要求序列已排序):
binary_search(first, last, val)
: 判断val
是否存在。lower_bound(first, last, val)
: 查找第一个不小于val
的元素。upper_bound(first, last, val)
: 查找第一个大于val
的元素。equal_range(first, last, val)
: 同时获取lower_bound
和upper_bound
。
D. 数值操作 (<numeric>
)
accumulate(first, last, init, op)
: 计算序列的和(或用op
进行累积)。iota(first, last, val)
: 用递增的值填充序列 (val
,val+1
,val+2
, …)。inner_product(first1, last1, first2, init)
: 计算内积。
4. 函数对象 (Functors) 与 Lambda 表达式
许多算法接受一个可调用对象(函数指针、函数对象、Lambda)来定制其行为。
- 函数对象 (Functor): 一个重载了
operator()
的类的对象。- 预定义函数对象 (
<functional>
):std::plus
,std::minus
,std::less
,std::greater
,std::logical_not
等。
- 预定义函数对象 (
- Lambda 表达式 (C++11+): 现代C++的首选,可以方便地在原地定义匿名函数。
- 语法:
[capture](params) -> return_type { body }
- 示例:
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
// 降序排序
- 语法:
std::function
(<functional>
): C++11引入的通用多态函数包装器,可以存储、复制和调用任何可调用目标。
总结
STL的强大之处在于其正交性:
- 容器负责存储数据。
- 算法负责处理数据。
- 迭代器作为两者之间的“胶水”,让任何算法都可以操作任何兼容的容器。
熟练掌握STL,特别是 vector
, string
, map
, unordered_map
, set
等常用容器,以及 sort
, find
, for_each
, transform
等核心算法,是成为一名高效C++程序员的关键。C++11及后续标准引入的Lambda表达式、范围for循环、auto
关键字等,进一步简化了STL的使用,使其更加强大和易用。
Last updated on