C++入门自学Day16-- STL容器类型总结

C++入门自学Day16-- STL容器类型总结

前言

本文把我们迄今为止学过的 STL 容器做一次系统性回顾,范围限定为:string、vector、deque、list、stack、queue、priority_queue。

目标不是把每个 API 都罗列完,而是从**"是什么 / 底层如何实现 / 常用接口示例 / 复杂度与迭代器失效规则 / 适用场景 / 常见坑"**这几方面,给出可直接引用到博客里的、便于工程决策的总结与对比。

主要内容概览

各容器简介(含底层实现直觉)

每个容器的关键接口、复杂度与迭代器失效规则(含示例)

容器间重要对比(性能、内存、语义)与选型建议

常见优化建议与工程注意点

总结(一句话回顾 + 决策要点)

各容器逐项详解

1. std::string

是什么 / 用途

专门的字符序列容器(basic_string),用于文本存储与操作,通常替代 C 风格字符串。

底层实现(直觉)

类似 vector:内部维护连续缓冲区 + size + capacity。很多实现有 小字符串优化(SSO),短字符串直接驻留在对象内部避免分配。

常用接口(示例)

cpp

复制代码

std::string s = "hello";

s += " world";

s.append("!");

auto sub = s.substr(1,3);

const char* c = s.c_str();

复杂度

operator\[\], at():O(1)

append/insert/erase:通常 O(n)(可能触发重新分配)

find:O(n)

迭代器/引用失效

发生重新分配(容量不足扩容)时,迭代器/指针/引用全部失效。

at() 提供边界检查;operator\[\] 不检查。

内存特性

连续内存,缓存友好;SSO 可减少小字符串分配。

适用场景

文本处理、拼接、格式化输出等。

常见坑 / 优化

频繁拼接时使用 reserve() 或 ostringstream;c_str() 在修改后可能失效。

2. std::vector

是什么 / 用途

最常用的动态数组容器,适合随机访问与高效遍历。

底层实现(直觉)

一段连续内存,维护 start/finish/end_of_storage(或 data,size,capacity)。

常用接口(示例)

cpp

复制代码

std::vector v;

v.reserve(100);

v.push_back(1);

v.emplace_back(2);

v.insert(v.begin()+1, 3);

v.erase(v.begin()+2);

复杂度

随机访问:O(1)

push_back:均摊 O(1),单次扩容 O(n)

中间 insert/erase:O(n)

迭代器/引用失效

扩容(realloc):所有迭代器/指针/引用失效。

insert/erase 在中间:invalidate 从变动位置到末尾的迭代器(引用/指针对被移动元素可能失效)。

push_back 若不触发扩容,一般不会失效。

内存特性

最省内存、缓存局部性最好,遍历最快。

适用场景

随机访问、批量处理、排序、频繁遍历的主容器。

常见坑 / 优化

尽量 reserve 已知大小;函数参数用 const&;使用 move / emplace 减少拷贝。

3. std::deque

是什么 / 用途

双端队列:既支持两端高效插入/删除,又支持随机访问。

底层实现(直觉)

分段缓冲(多个固定大小 block)+ 中央索引表(map of blocks),逻辑上连续,物理上分段。

常用接口(示例)

cpp

复制代码

std::deque dq;

dq.push_front(1); dq.push_back(2);

dq.pop_front(); dq.pop_back();

dq[2]; // 随机访问

复杂度

push_front / push_back:O(1)(均摊)

随机访问:O(1)(比 vector 稍慢常数)

中间插入/删除:O(n)

迭代器/引用失效

两端插入/删除 :实现相关,但保守地认为迭代器 可能 失效;引用/指针通常保持有效(对已存在元素,非被删元素)。

中间插入/删除:可能会使迭代器与引用失效(会移动元素)。

内存特性

每个 block 分配;相比 vector 有额外开销;缓存局部性介于 vector 和 list 之间。

适用场景

需要在两端频繁操作并且还要随机访问(如滑动窗口、双端缓冲)。

常见坑 / 优化

不要依赖迭代器在两端操作后仍然有效;中间大量插入应考虑其它数据结构。

4. std::list(双向链表)

是什么 / 用途

节点式双向链表,适合在任意位置 O(1) 插入/删除(已知位置)。

底层实现(直觉)

每个节点包含数据 + 前驱/后继指针;每节点单独分配(allocator)。

常用接口(示例)

cpp

复制代码

std::list L = {1,2,3};

auto it = std::next(L.begin(), 1);

L.insert(it, 42); // O(1)

L.erase(it); // O(1)

L.splice(other, it_begin, it_end); // O(1) 节点移动

复杂度

插入/删除(已知迭代器位置):O(1)

随机访问:O(n)

迭代器/引用失效

插入不使其他节点迭代器失效;erase(it) 只使被删迭代器失效。迭代器稳定性最好(除被删元素)。

内存特性

每节点开销大(指针 + 分配器元信息);缓存友好性差。

适用场景

频繁中间插删,且需要迭代器/引用稳定(比如需要 splice 的场景)。

常见坑 / 优化

不要为了避免对象移动就盲用 list;很多情况下 vector + move/index 更快。避免在性能敏感场景随意使用 list。

5. std::stack(适配器)

是什么 / 用途

容器适配器,提供 LIFO(后进先出)接口:push/pop/top。并非独立容器。

底层实现

默认用 deque,也可以指定 vector 或 list 作为底层(模板参数)。

常用接口(示例)

cpp

复制代码

std::stack st;

st.push(1); st.push(2);

auto t = st.top(); st.pop();

复杂度

push/pop/top:O(1)(底层容器决定细节)。

迭代器/引用

stack 不提供迭代器;top() 返回的引用在 pop() 后失效。

适用场景

括号匹配、DFS(显式非递归)、撤销栈等。

常见坑

stack 隐藏了底层容器,若需要遍历或随机访问直接使用底层容器(deque/vector)。

6. std::queue(适配器)

是什么 / 用途

容器适配器,提供 FIFO(先进先出)接口:push/pop/front/back。

底层实现

默认 deque,也可以用 list。不能用 vector(无 pop_front)。

常用接口(示例)

cpp

复制代码

std::queue q;

q.push(1); q.push(2);

auto f = q.front(); q.pop();

复杂度

基本操作 O(1)(由底层容器决定)。

迭代器/引用

queue 不提供迭代器;front()/back() 的引用在 pop() 后失效。

适用场景

BFS、任务队列、消息转发等。

常见坑

无 clear()(可通过 swap 空队列或循环 pop 实现);并发场景需加锁或用并发队列。

7. std::priority_queue(优先队列)

是什么 / 用途

按优先级出队的容器适配器(heap 实现),top() 返回优先级最高元素。

底层实现

默认底层是 std::vector,通过二叉堆算法实现(make_heap/push_heap/pop_heap)。

常用接口(示例)

cpp

复制代码

std::priority_queue pq; // max-heap

pq.push(3); pq.push(1); pq.pop();

// min-heap:

std::priority_queue, std::greater> minpq;

复杂度

top():O(1)

push/pop:O(log n)

从范围构造(make_heap):O(n)

迭代器/引用

priority_queue 不提供迭代器;底层 vector 的 reallocate 会使指针/引用失效。

适用场景

Dijkstra、事件驱动仿真、top-k 问题、调度器等。

常见坑

堆不是稳定的;若需要稳定性需要额外的 tiebreaker(如时间戳)。

容器比较(关键维度对照)

容器

内存布局

随机访问

头/尾插删

中间插删

迭代器稳定性

缓存局部性

典型适用

string

连续

O(1)

末尾快

O(n)

扩容会失效

很好

文本处理

vector

连续

O(1)

末尾快

O(n)

扩容会失效

最好

大多数序列需求(默认)

deque

分段连续

O(1)

两端快

O(n)

两端操作迭代器可能失效

较好

两端操作 + 随机访问

list

节点

不支持

O(1)(已知位)

O(1)(已知位)

插入不失效,erase 仅失效被删

频繁中间插删、splice

stack (adapter)

由底层决定

依赖底层

依赖底层

---

不提供迭代器

---

LIFO 语义

queue (adapter)

由底层决定

依赖底层

依赖底层

---

不提供迭代器

---

FIFO 语义

priority_queue

heap on vector

不直接支持

push: O(log n)

---

不提供迭代器

受 vector 影响

优先级调度、top-k

选型与工程化建议(快速决策要点)

默认首选 vector:能满足多数场景且性能最好。

两端操作频繁且仍需随机访问 → deque。

需要在中间大量插删且需要迭代器稳定 → list(先确认是否真需要)。

需要 FIFO / LIFO / priority 语义 → queue / stack / priority_queue(适配器,底层通常 deque/vector)。

频繁扩容场景:尽早 reserve();对 priority_queue 在已知元素构造时使用区间构造(O(n))。

避免长期持有迭代器/引用:任何修改后应谨慎重新获取。

避免误用 list 来"避免移动对象":常见误区,vector 的移动/交换往往更快且内存更经济。

并发使用:STL 容器非线程安全,生产-消费场景加锁或使用专用并发结构。

常见优化技巧(实践可复制)

对 vector/string:reserve(n) 避免扩容;用 emplace_back 原地构造;传参使用 const T&,返回使用移动语义。

对 unordered_*:若能估算元素数用 reserve() 预留桶,减少 rehash。

对 priority_queue:若已知全部数据,优先用区间构造(make_heap)而不是逐个 push。

对 list:用 splice 在容器间 O(1) 移动节点,避免元素拷贝。

总是测 perf:关键路径用基准测试(profiling)胜过直觉。

总结(结论性回顾)

string / vector / deque:属于连续或分段连续存储 ,以缓存友好与随机访问为主;其中 vector 是通用默认容器,deque 平衡了两端操作;string 专用于字符。

list:节点式容器,优势在 中间插删与迭代器稳定性,代价是内存开销与差的缓存局部性。

stack / queue / priority_queue:语义清晰的容器适配器,它们隐藏底层细节以暴露特定接口:stack/queue操作 O(1)(底层决定),priority_queue 基于堆(push/pop O(log n))。

工程实践要点:默认用 vector,明确定义需求(随机访问 vs. 中间插删 vs. 两端操作 vs. 优先级)再选型;始终关注迭代器失效规则与内存/缓存影响。

相关推荐

火车站仓库归哪里管理呢
beat365中国

火车站仓库归哪里管理呢

📅 02-14 👁️ 2137
【Audemars Piguet爱彼手表型号15710ST.OO.A002CA.02皇家橡树离岸型系列价格查询】官网报价
蚂蚁花呗可以在商场用吗?使用规则看这里
365外网足球

蚂蚁花呗可以在商场用吗?使用规则看这里

📅 12-07 👁️ 6509
秋分是什么时候?秋分是什么意思?
365bet苹果版

秋分是什么时候?秋分是什么意思?

📅 08-21 👁️ 6172
王者荣耀秦始皇
365bet苹果版

王者荣耀秦始皇

📅 01-01 👁️ 9246
4399游戏盒怎么刷盒币啊!
beat365中国

4399游戏盒怎么刷盒币啊!

📅 07-15 👁️ 1003
竟然造句
365外网足球

竟然造句

📅 01-28 👁️ 5938
探索Python中的时间逆流:实现时钟倒退算法的技巧与实例
烽火战国怎么点亮?操作方法揭秘!
365外网足球

烽火战国怎么点亮?操作方法揭秘!

📅 10-26 👁️ 1716