C++ STL六大核心容器详解与性能对比

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 容器选择决策树

面对具体问题时,可以按照以下思路选择容器:

  1. 需要随机访问吗?

    • 是 → 考虑vector或deque
    • 否 → 进入下一步
  2. 需要频繁在中间插入/删除吗?

    • 是 → 考虑list
    • 否 → 进入下一步
  3. 需要自动排序吗?

    • 是 → 考虑set
    • 否 → 进入下一步
  4. 需要快速判断元素是否存在?

    • 是 → 考虑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的性能优化技巧

  1. 预分配空间:使用reserve()提前分配足够空间,避免多次扩容
cpp复制vector<int> nums;
nums.reserve(1000000);  // 预分配100万空间
for(int i=0; i<1000000; ++i) {
    nums.push_back(i);  // 不会触发扩容
}
  1. 正确使用emplace_back:比push_back更高效,直接在容器内构造对象
cpp复制vector<pair<int, string>> v;
v.emplace_back(1, "one");  // 直接构造,避免临时对象
  1. 避免在vector中间插入:中间插入是O(n)操作,频繁操作应考虑list

vector的扩容策略:大多数实现中,当vector需要扩容时,会按照当前容量的1.5或2倍进行扩容。这个操作会导致所有元素被复制到新内存,因此频繁扩容会严重影响性能。

3.3 vector的常见陷阱

  1. 迭代器失效:在修改vector后,之前获取的迭代器可能失效
cpp复制vector<int> v = {1, 2, 3, 4};
auto it = v.begin() + 2;
v.push_back(5);  // 可能导致扩容,使it失效
// cout << *it << endl;  // 危险!可能崩溃
  1. 下标越界: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;
}
  1. 误用size_type:size()返回size_type,通常是无符号类型
cpp复制vector<int> v;
// for(int i=0; i<v.size()-

内容推荐

已经到底了哦
已经到底了哦