文章目录
- 前言
- [一、STL 整体认知](#一、STL 整体认知)
- [二、三大类容器底层原理 + 选型标准](#二、三大类容器底层原理 + 选型标准)
-
- 序列式容器(有序存储)
- 关联容器(红黑树|自动有序)
-
- [map / set](#map / set)
- 无序关联容器(哈希表|极速查找)
-
- [unordered_map / unordered_set](#unordered_map / unordered_set)
- 终极容器选型口诀
- [三、vector 扩容机制](#三、vector 扩容机制)
- 四、迭代器与【迭代器失效】
- [五、STL 算法库 + Lambda](#五、STL 算法库 + Lambda)
-
- [sort 排序](#sort 排序)
- [find 查找](#find 查找)
- [for_each 遍历](#for_each 遍历)
- 六、序列化入门
- 七、本阶段全部易错点汇总
- 八、阶段学习感悟
前言
上一阶段我们补齐了 C++ 语言底层坑点:const 体系、深浅拷贝、移动语义、noexcept、悬垂指针/引用。
解决了「为什么代码会崩溃、内存泄漏、性能差」的底层问题。
本阶段进入工程实战核心能力:STL 标准库 + 序列化能力。
在企业 C++ 开发中:90% 的业务数据处理、遍历、查找、排序、存储、网络传输全部依赖 STL。
同时本阶段对标公司 C++ 招聘 JD 核心要求:熟练内存管理、数据结构、序列化、调试能力。
本文为第二阶段完整复盘,包含:容器底层原理、容器选型、迭代器失效致命坑、算法实战、序列化原理与踩坑大全,适合长期复习、面试背诵。
一、STL 整体认知
STL(标准模板库)分为三大模块:
- 容器:帮我们存数据(替代数组、结构体数组)
- 迭代器:通用"智能指针",统一所有容器遍历方式
- 算法:排序、查找、遍历、过滤,搭配 Lambda 极简开发
外加岗位刚需:序列化能力(内存对象 ↔ 文件/网络字节流)。
二、三大类容器底层原理 + 选型标准
STL 容器不是随便用的,选错容器直接导致性能翻倍变差。
序列式容器(有序存储)
vector(动态数组|默认首选)
底层:一整块连续堆内存。
优点:随机访问 O(1)、缓存命中率极高、尾部增删极快。
缺点:头部/中间插入删除慢、扩容会整体搬迁内存。
适用场景:90% 业务场景的默认选择。
deque(分段双端队列)
底层:分段数组。
deque 不是一整块内存,是:
- 多个小块连续内存存储数据
- 一张中控索引表记录每一块内存地址
对外逻辑连续,对内碎片化分段存储。
核心优势:头尾插入删除 O(1),无需搬迁整体数据。
缺点:随机访问比 vector 慢、内存不规整。
场景:队列业务、频繁头尾增删。
list(双向链表)
底层链表,不连续内存,不支持随机访问,几乎不用。
关联容器(红黑树|自动有序)
map / set
- 底层红黑树,查询/插入/删除
O(logN) - key 自动升序排序、key 唯一不重复
场景:需要有序键值对、有序去重集合。
无序关联容器(哈希表|极速查找)
unordered_map / unordered_set
- 底层哈希表,平均查找
O(1) - 无序、速度最快
场景:海量数据查找、缓存、映射关系,不要求有序。
终极容器选型口诀
- 普通存数据、遍历多、随机访问多 → vector
- 频繁头尾增删 → deque
- 需要 Key 有序 → map
- 需要 Key 极速查找 → unordered_map
三、vector 扩容机制
vector 有两个关键属性:
- size:当前元素个数
- capacity:当前总容量(最多能存多少,不扩容)
当 size == capacity,再次 push_back 触发扩容:
- 开辟一块更大的新连续内存
- 把旧元素搬运到新内存
- 释放旧内存
重点联动上一阶段知识点,扩容搬运时:
- 如果移动构造带 noexcept → 直接移动资源(极速)
- 如果没有 noexcept → 降级为深拷贝(性能暴跌)
这就是为什么工程规范强制移动构造加 noexcept。
四、迭代器与【迭代器失效】
迭代器本质:容器的通用智能指针,用来遍历所有容器。
迭代器失效本质:容器内存发生变更后,旧迭代器指向已销毁内存,属于悬垂指针,访问直接崩溃/乱码。
vector 失效规则
触发全部失效:
- push_back 触发扩容
- reserve(更大容量) 触发扩容
- resize 触发扩容
关键解惑:reserve 不一定失效。只有 reserve 的值大于当前 capacity 才会扩容、失效迭代器。如果更小或相等:不做任何操作,迭代器完全有效。
局部失效:
erase / insert 后,当前位置及后面迭代器全部失效。
map/unordered_map 失效规则
只有被删除的那个迭代器失效,其他全部有效。
相比 vector 极其安全。
vector 安全删除写法
c++
auto it = vec.begin();
while (it != vec.end())
{
if (*it % 2 == 0)
{
// erase 返回下一个有效迭代器
it = vec.erase(it);
}
else
{
++it;
}
}
五、STL 算法库 + Lambda
工程禁止手写 for 循环无脑遍历,优先使用 <algorithm> + Lambda。
常用三大算法:
sort 排序
c++
// 降序
std::sort(vec.begin(), vec.end(), [](int a, int b){
return a > b;
});
find 查找
c++
auto it = std::find(vec.begin(), vec.end(), 15);
if (it != vec.end()) {
// 找到
}
for_each 遍历
c++
std::for_each(vec.begin(), vec.end(), [](int v){
std::cout << v << " ";
});
六、序列化入门
序列化:内存对象 → 可存储/可传输字节流
反序列化:字节流 → 还原内存对象
二进制序列化
直接将 POD 简单结构体内存二进制写入文件。
适用:仅纯基础类型结构体(int/short 等)
致命禁忌:不能包含 string、vector 等容器,只会写入指针地址,数据彻底损坏。
本地踩坑完整复盘
之前读出数据为 0、看不到 bin 文件,原因:
- CLion 默认工作目录在 cmake-build-debug,文件生成在构建目录,不是源码目录
- 未判断文件打开失败,流异常静默失败,读出空数据
- 未加二进制打开模式,Windows 会损坏字节流
工程真正方案
原生二进制序列化仅做学习理解,真实项目使用:JSON / Protobuf。
七、本阶段全部易错点汇总
- 分不清 deque 分段内存与 vector 整块内存
- 误以为 reserve 一定导致迭代器失效(只有扩容才失效)
- erase 不接收返回值,迭代器残留失效
- 滥用 map/unordered_map,不按场景选型
- 范围 for 循环不加 const&,大量无用拷贝
- 对含 string/vector 的结构体直接二进制序列化(必崩)
- 不判断文件流是否打开成功,导致序列化静默失败
八、阶段学习感悟
如果说上一阶段是搞定 C++ 底层内存安全,那这一阶段就是掌握 C++ 工程生产力。
STL 是 C++ 开发者的武器库:容器管理数据、迭代器统一遍历、算法高效处理业务、序列化打通持久化与网络传输。
前端开发习惯框架封装,而 C++ 需要我们清晰知道每一段代码的内存行为、性能损耗、风险点。
本阶段学完,已经具备企业级基础编码能力:
- 能根据业务精准选择容器
- 彻底规避迭代器失效崩溃
- 熟练算法+Lambda 现代化写法
- 理解序列化核心原理与工程坑点
下一阶段:现代 C++ 核心特性、类型转换、异常处理、文件 IO,彻底夯实工程编码规范。