容器库是类模板与算法的汇集,允许程序员简单地访问常见数据结构,例如队列、链表和栈。有两 (C++11 前)三 (C++11 起)类容器:
其中每一个被设计为支持不同的一系列操作。
容器管理着为其元素分配的存储空间,并提供成员函数来直接访问或通过迭代器(具有类似于指针的属性的对象)访问它们。
许多容器有几个共同的成员函数,并且共享功能。决定使用哪种类型的容器来满足特定需求通常不仅仅取决于容器提供的功能,还取决于其某些成员的效率(复杂性)。对于序列容器来说尤其如此,它在插入/删除元素和访问它们之间的复杂性上提供了不同的权衡。
顺序容器
顺序容器实现能按顺序访问的数据结构。
|
静态的连续数组 (类模板) |
|
动态的连续数组 (类模板) |
|
双端队列 (类模板) |
|
单链表 (类模板) |
|
双链表 (类模板) |
关联容器
关联容器实现能快速查找(O(log n) 复杂度)的数据结构。
|
唯一键的集合,按照键排序 (类模板) |
|
键值对的集合,按照键排序,键是唯一的 (类模板) |
|
键的集合,按照键排序 (类模板) |
|
键值对的集合,按照键排序 (类模板) |
无序关联容器 (C++11 起)
无序关联容器提供能快速查找(均摊 O(1) ,最坏情况 O(n) 的复杂度)的无序(哈希)数据结构。
|
唯一键的集合,按照键生成散列 (类模板) |
|
键值对的集合,按照键生成散列,键是唯一的 (类模板) |
|
键的集合,按照键生成散列 (类模板) |
|
键值对的集合,按照键生成散列 (类模板) |
容器适配器
容器适配器为顺序容器提供了不同的接口。
|
适配一个容器以提供栈(LIFO 数据结构) (类模板) |
|
适配一个容器以提供队列(FIFO 数据结构) (类模板) |
|
适配一个容器以提供优先级队列 (类模板) |
|
调整容器以提供按键排序的唯一键集合 (类模板) |
|
调整容器以提供按唯一键排序的键值对集合 (类模板) |
|
调整容器以提供按关键字排序的关键字集合 (类模板) |
|
调整容器以提供按关键字排序的键值对集合 (类模板) |
迭代器失效
只读方法决不会使迭代器或引用失效。修改容器内容的方法可能会使迭代器和/或引用失效,如下表所示。
此处插入指代任何添加一或多个元素到容器的方法,而擦除指代任何从容器移除一或多个元素的方法。
除非另有规定(显式地或通过根据其他函数定义函数), 否则将容器作为参数传递给库函数不会使迭代器失效或更改容器内对象的值。
尾后迭代器(past-the-end iterator)需要特别留意。通常像指向未被擦除元素的正常迭代器一般使此迭代器失效。所以 std::set::end 永远不会失效,std::unordered_set::end 只有在重哈希(rehash)时会失效 (C++11 起), std::vector::end 总是会失效(因为它始终在被修改元素后出现),以此类推。
有一个例外:删除 std::deque 末元素的擦除操作会使尾后迭代器失效,尽管它不是容器的被擦除元素(或者说根本不是元素)。与 std::deque 迭代器的通用规则结合后,最终结果是修改操作中只有“删除首元素”(而不是“删除末元素”) 不会 使std::deque::end 失效。
线程安全
- 能同时在不同容器上由不同线程调用所有容器函数。更广泛而言, C++ 标准库函数不读取能通过其他线程访问的对象,除非这些对象能直接或间接地经由函数参数,包含 this 指针访问。
- 能同时在同一容器上由不同线程调用 const 成员函数。而且,成员函数
begin() 、end() 、rbegin() 、rend() 、front() 、back() 、data() 、find() 、lower_bound() 、upper_bound() 、equal_range() 、at() 和除了关联容器中的 operator[] 对于线程安全的目标表现如同 const (即它们也能同时在同一容器上由不同线程调用)。更广泛而言,C++ 标准库函数不会修改对象,除非这些对象能直接或间接地经由函数参数,包含 this 指针访问。
- 同一容器中不同元素能由不同线程同时修改,除了 std::vector<bool> 的元素(例如, std::future 对象的 vector 能从多个线程接收值)。
- 迭代器操作(例如自增迭代器)读但不修改底层容器,而且能与同一容器上的其他迭代器操作同时由 const 成员函数执行。会使任何迭代器失效的容器操作都会修改容器,且不能与任何在既存迭代器上的操作同时执行,即使这些迭代器尚未失效。
- 同一容器上的元素可以同时由不指定为访问这些元素的函数修改。更广泛而言, C++ 标准库函数不间接读取能从它的参数访问的对象(包含容器的其他对象),除非其规定要求如此。
- 任何情况下,容器操作(还有算法,或其他 C++ 标准库函数)可于内部并行化,只要不更改用户可见的结果(例如 std::transform 可并行化,但指定了按顺序观览序列的每个元素的 std::for_each 不行)
|
(C++11 起) |
函数表格
注意:std::basic_string 不被标准视为容器,但由于其相似性,其行为与容器非常相似。为方便起见,此处将其列为“伪容器”(Pseudo container)。
|
- C++03 起存在的函数
|
|
- C++11 起存在的函数
|
|
- C++17 起存在的函数
|
|
- C++20 起存在的函数
|
|
- C++23 起存在的函数
|
成员函数表格
- 注意:两个不同的 extract 行中的函数具有不同的含义和语法:
- ↑ 例如,node_type extract(const_iterator) 或 node_type extract(Key&)
- ↑ 例如,container_type extract() &&
非成员函数表
< 、 <= 、 > 、 >= 及 != 运算符分别从 operator<=> 与 operator== 合成。
|
(C++20 起) |
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告
|
应用于
|
出版时的行为
|
正确行为
|
LWG 51
|
C++98
|
容器迭代器可能会由于任意库操作而失效
|
只有在指定情况下会失效
|
参阅
C++ 已命名的要求(requirement):
|
数值数组,数组掩码和数组切分 (类模板) |
|
存储并操作字符序列 (类模板) |
|
只读的字符串视图 (类模板) |
|
对象的连续序列上的无所有权视图 (类模板) |
|
多维非拥有数组视图 (类模板) |