标准模板库
主要来源:概述 | C++ STL Tutorial
容器
- 容器:是一种可存储和管理对象集合的数据结构,直接实现了基本的数据存储和访问机制。例如,
vector
是动态数组,list
是双向链表,set
是有序集合,unordered_map
是哈希表形式的键值对集合。 它们各自按照不同的数据结构原理,来组织和存储元素。 - 容器适配器:是对已有的容器进行封装,改变其接口以满足特定的需求,本质上是一种包装器。它并不重新实现数据存储和管理机制,而是依赖于其他容器来完成这些工作。常见的容器适配器有
stack
(栈)、queue
(队列)、priority_queue
(优先队列)。
容器:
flowchart TDG{"*指向元素的指针或迭代器,无论添加或删除元素,始终有效<br>**双向链接"}subgraph 序列容器H{动态大小}H -- 否 --> arrayH -- 是 --> I{保持有序吗?}I -- 否 --> J{是否在中间插入/删除元素?}J -- 是 --> K{频繁遍历吗?}J -- 否 --> L{是否在前端插入/删除元素?}K -- 是 --> M{位置是否持久*?}K -- 否 --> N{大小变化大吗?}L -- 是 --> dequeL -- 否 --> vectorM -- 是 --> llist**N -- 否 --> vectorN -- 是 --> llist**endsubgraph 有序容器I -- 是 --> O{主要用途?}O -- 中序遍历 --> P[vector<sorted> 或 flat_set]O -- 按键搜索 --> Q{允许重复元素吗?}Q -- 否 --> R{键映射到值吗?}Q -- 是 --> S{键映射到值吗?}R -- 是 --> mapR -- 否 --> setS -- 是 --> multimapS -- 否 --> multisetend
容器适配器:
flowchart TDsubgraph 自适应容器A{顺序是否重要?}A -- 是 --> B{后进先出}A -- 否 --> C{先进先出}B -- 是 --> stackC -- 是 --> queueC -- 否 --> priority_queueendsubgraph 无序容器D{允许重复元素吗?}A -- 否 --> DD -- 否 --> E{键映射到值吗?}D -- 是 --> F{键映射到值吗?}E -- 是 --> unordered_mapE -- 否 --> unordered_setF -- 是 --> unordered_multimapF -- 否 --> unordered_multisetend
注意点
对于vector
,emplace_back
有更好的性能,应当优先使用.
string_view
在没有需要修改字符串的情况下,比如string
更好
迭代器
const迭代器
简单说,const_iterator
是一种 "只读" 的迭代器,它指向的数据不能被修改。就像你只能看一个东西,但不能碰它。而普通的 iterator
是 "可写" 的,既能看也能改。
-
防止意外修改数据(编译器会报错)
-
让代码意图更清晰(别人一看就知道这里不会修改数据)
以前(C++98)这个 const_iterator
不好用,主要问题是:
- 不容易创建(想从普通容器里拿到它很麻烦)
- 很多操作不支持(比如想在找到的位置插入数据,C++98 不接受用
const_iterator
作为位置参数)
所以那时候大家宁可用普通 iterator,就算其实不需要修改数据。
从 C++11 开始,这些问题都解决了:
- 容器新增了
cbegin()
、cend()
方法,直接就能拿到const_iterator
- 插入、删除等操作也支持用 const_iterator 来指定位置了
比如原来的代码可以改成这样(更简单且安全):
// 用 cbegin()/cend() 得到 const_iterator
auto it = std::find(values.cbegin(), values.cend(), 1983);
values.insert(it, 1998); // 现在可以直接用 const_iterator 了
C++11 还有个小遗憾:对于一些特殊的数据结构(比如原生数组),没有提供全局的 cbegin()
、cend()
函数。C++14 补上了这个漏洞,让代码可以更通用。
总之:
- 写代码时,只要不需要修改迭代器指向的数据,就优先用
const_iterator
- 获取迭代器时,优先用
cbegin()
、cend()
(而不是普通的begin()
、end()
) - 在通用代码中,优先用全局的
begin()
、end()
、cbegin()
等函数(而不是容器的成员函数)
这样做能让代码更安全、更清晰、更通用。