C++ 容器(Containers)详解
在 C++ 标准库(STL, Standard Template Library)中,容器(Container) 是用于存储和管理一组对象的核心组件。它们封装了数据结构与常用操作,使开发者无需手动管理内存或实现底层算法,从而写出更安全、简洁、高效的代码。所有标准容器都是类模板(class templates)!
一、什么是容器?
容器是 C++ 标准库提供的、通用的类模板(class templates),用于组织和操作一组元素。
容器的核心特征:
- 存储多个同类型元素 (如
int、string、自定义对象等); - 自动管理内存 (无需手动
new/delete); - 提供统一接口 :如
.size()、.empty()、.begin()、.end(); - 支持迭代器遍历 和 STL 算法 (如
std::sort,std::find); - 类型安全:编译期检查,避免越界(部分容器)。
注意:容器是 标准库预定义的类,不是用户"自定义类"------你使用它们,而不是从零实现它们。
二、容器的分类
C++ 标准将容器分为三大类:
1. 序列容器(Sequence Containers)
元素按线性顺序存储,位置由插入顺序决定。
| 容器 | 特点 | 典型用途 |
|---|---|---|
std::vector |
动态数组,支持随机访问,尾部插入快 | 通用首选,替代 C 数组 |
std::deque |
双端队列,首尾插入/删除快 | 需要双向高效操作 |
std::list |
双向链表,任意位置插入/删除快 | 频繁中间插入/删除 |
std::forward_list |
单向链表,内存开销最小 | 内存敏感场景 |
std::array |
固定大小数组(编译期确定) | 替代 C 风格数组,更安全 |
std::string |
特化的字符序列容器 | 存储和操作文本 |
std::string虽然用于文本,但技术上属于序列容器!
2. 关联容器(Associative Containers)
基于红黑树 实现,元素按键自动排序,查找效率为 O(log n)。
| 容器 | 键是否唯一 | 是否有序 | 元素类型 |
|---|---|---|---|
std::set |
唯一 | ✅ | Key |
std::multiset |
允许重复 | ✅ | Key |
std::map |
唯一 | ✅ | pair<const Key, Value> |
std::multimap |
允许重复 | ✅ | pair<const Key, Value> |
🔒
map的键(first)是const,不可修改!
3. 无序关联容器(Unordered Associative Containers)
基于哈希表 实现,元素无序,平均查找效率为 O(1)。
| 容器 | 键是否唯一 | 是否有序 |
|---|---|---|
std::unordered_set |
唯一 | ❌ |
std::unordered_multiset |
允许重复 | ❌ |
std::unordered_map |
唯一 | ❌ |
std::unordered_multimap |
允许重复 | ❌ |
⚡ 适合需要快速查找且不关心顺序的场景。
4. 容器适配器(Container Adapters)
基于其他容器封装,限制接口以提供特定行为。
| 适配器 | 默认底层容器 | 行为 |
|---|---|---|
std::stack |
deque |
后进先出(LIFO) |
std::queue |
deque |
先进先出(FIFO) |
std::priority_queue |
vector |
按优先级出队(最大堆) |
🛠️ 适配器不支持迭代器,不能遍历!
三、为什么使用容器? vs C 风格数组
| 对比项 | C 风格数组 / 指针 | C++ 容器 |
|---|---|---|
| 内存管理 | 手动(易泄漏、越界) | 自动(RAII) |
| 大小动态调整 | ❌ 固定大小 | ✅ vector、string 等可自动扩容 |
| 安全性 | 低(无边界检查) | 高(.at() 有检查,迭代器安全) |
| 功能 | 仅存储 | 内置丰富操作(push_back, find, sort 等) |
| 泛型支持 | ❌ | ✅ 模板 + STL 算法 |
🚫 示例:C 风格危险代码
cchar buf[5]; strcpy(buf, "Hello!"); // 缓冲区溢出!✅ C++ 安全替代
cppstd::string s = "Hello!"; // 自动分配 7 字节,安全!
四、容器的通用操作示例
cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main() {
// 1. 创建容器
std::vector<int> vec = {3, 1, 4, 1, 5};
// 2. 遍历(三种方式)
for (size_t i = 0; i < vec.size(); ++i) // 索引
std::cout << vec[i] << " ";
for (auto it = vec.begin(); it != vec.end(); ++it) // 迭代器
std::cout << *it << " ";
for (const auto& x : vec) // 范围 for
std::cout << x << " ";
// 3. 使用 STL 算法
std::sort(vec.begin(), vec.end()); // 排序
auto pos = std::find(vec.begin(), vec.end(), 4); // 查找
// 4. string 作为容器
std::string s = "hello";
std::reverse(s.begin(), s.end()); // → "olleh"
return 0;
}
五、常见误区澄清
| 误区 | 正确理解 |
|---|---|
| "容器是自己写的类" | ❌ 容器是 标准库提供的类模板 |
| "所有类都是容器" | ❌ 只有满足容器要求的类才是(如提供 begin()/end()) |
"string 不是容器" |
❌ std::string 是 标准序列容器 |
| "容器一定防止越界" | ⚠️ operator[] 不检查,.at() 才抛异常 |
六、总结
- 容器是 C++ 现代编程的基石,应优先于 C 风格数组和指针;
std::vector和std::string是最常用的容器;- 选择容器的原则:
- 需要随机访问?→
vector - 频繁中间插入?→
list - 快速按键查找?→
unordered_map - 只需栈/队列行为?→
stack/queue - 结合
auto、范围for、STL 算法,写出简洁安全的代码!
记住:在 C++ 中,能用标准容器,就不要手写数组或链表!