1. C++ STL容器入门指南:从零开始掌握六大核心容器
作为一名C++开发者,STL容器是我们日常编码中最常用的工具之一。但很多初学者在面对vector、list、set等不同容器时,常常感到困惑:它们有什么区别?什么时候该用哪个?今天我就结合自己多年的开发经验,带大家彻底搞懂这六大核心容器。
STL(Standard Template Library)是C++标准库的重要组成部分,而容器则是STL中最基础也是最实用的部分。理解并熟练使用这些容器,能让你写出更高效、更优雅的C++代码。我们将按照使用频率从高到低的顺序,逐一解析每个容器的特性、适用场景和最佳实践。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 容器基础概念与选择指南
2.1 容器核心特性对比
在深入每个容器之前,我们先整体了解下六大容器的核心特性:
| 容器类型 | 底层实现 | 是否有序 | 允许重复 | 随机访问 | 时间复杂度 | 典型用途 |
|---|---|---|---|---|---|---|
| vector | 动态数组 | 有序 | 允许 | O(1) | 插入/删除尾部O(1) | 默认选择,通用存储 |
| string | 字符动态数组 | 有序 | 允许 | O(1) | 同vector | 字符串处理 |
| deque | 分段连续数组 | 有序 | 允许 | O(1) | 头尾操作O(1) | 双端队列需求 |
| list | 双向链表 | 有序 | 允许 | O(n) | 插入/删除O(1) | 频繁中间插入删除 |
| set | 红黑树 | 有序 | 不允许 | 不支持 | 操作O(log n) | 自动排序+去重 |
| unordered_set | 哈希表 | 无序 | 不允许 | 不支持 | 平均O(1) | 快速存在性判断 |
2.2 容器选择决策树
面对具体问题时,可以按照以下思路选择容器:
-
需要随机访问吗?
- 是 → 考虑vector或deque
- 否 → 进入下一步
-
需要频繁在中间插入/删除吗?
- 是 → 考虑list
- 否 → 进入下一步
-
需要自动排序吗?
- 是 → 考虑set
- 否 → 进入下一步
-
需要快速判断元素是否存在?
- 是 → 考虑unordered_set
- 否 → 默认选择vector
实际开发中,vector能满足80%以上的需求,这也是为什么它被称为"默认选择"。
3. vector:C++中最常用的动态数组
3.1 vector的基本用法
vector是C++中最基础也是最重要的容器,它提供了动态数组的功能,能够自动管理内存,让我们无需手动处理数组的扩容问题。
cpp复制#include <vector>
#include <iostream>
using namespace std;
int main() {
// 五种初始化方式
vector<int> v1; // 空vector
vector<int> v2(5); // 5个0
vector<int> v3(5, 42); // 5个42
vector<int> v4 = {1, 2, 3, 4}; // 初始化列表(C++11)
vector<int> v5(v4.begin(), v4.end()); // 通过迭代器初始化
// 常用操作
v4.push_back(5); // 尾部添加元素
v4.pop_back(); // 删除尾部元素
v4.insert(v4.begin() + 2, 10); // 在第三个位置插入10
v4.erase(v4.begin() + 1); // 删除第二个元素
// 容量相关
cout << "大小: " << v4.size() << endl;
cout << "容量: " << v4.capacity() << endl;
v4.shrink_to_fit(); // 减少容量到刚好容纳元素
v4.reserve(100); // 预分配100个元素空间
return 0;
}
3.2 vector的性能优化技巧
- 预分配空间:使用reserve()提前分配足够空间,避免多次扩容
cpp复制vector<int> nums;
nums.reserve(1000000); // 预分配100万空间
for(int i=0; i<1000000; ++i) {
nums.push_back(i); // 不会触发扩容
}
- 正确使用emplace_back:比push_back更高效,直接在容器内构造对象
cpp复制vector<pair<int, string>> v;
v.emplace_back(1, "one"); // 直接构造,避免临时对象
- 避免在vector中间插入:中间插入是O(n)操作,频繁操作应考虑list
vector的扩容策略:大多数实现中,当vector需要扩容时,会按照当前容量的1.5或2倍进行扩容。这个操作会导致所有元素被复制到新内存,因此频繁扩容会严重影响性能。
3.3 vector的常见陷阱
- 迭代器失效:在修改vector后,之前获取的迭代器可能失效
cpp复制vector<int> v = {1, 2, 3, 4};
auto it = v.begin() + 2;
v.push_back(5); // 可能导致扩容,使it失效
// cout << *it << endl; // 危险!可能崩溃
- 下标越界:operator[]不检查边界,at()会检查但性能稍差
cpp复制vector<int> v = {1, 2, 3};
// v[5] = 10; // 未定义行为
try {
v.at(5) = 10; // 抛出std::out_of_range异常
} catch(const exception& e) {
cerr << e.what() << endl;
}
- 误用size_type:size()返回size_type,通常是无符号类型
cpp复制vector<int> v;
// for(int i=0; i<v.size()-
