C++ 进阶第二阶段总结|STL容器全家桶、迭代器失效、STL算法、序列化入门(工程必备)

文章目录

前言

上一阶段我们补齐了 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 触发扩容:

  1. 开辟一块更大的新连续内存
  2. 把旧元素搬运到新内存
  3. 释放旧内存

重点联动上一阶段知识点,扩容搬运时:

  • 如果移动构造带 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,彻底夯实工程编码规范。

相关推荐
过河卒_zh15667662 小时前
再更新!人工智能安全治理指引性文件第三版发布!
人工智能·算法·aigc·生成式人工智能·算法备案
伯恩bourne2 小时前
对比 @Configuration(Full) vs @Component(Lite)
java·开发语言
软件开发技术深度爱好者2 小时前
ndexTTS-2和 CosyVoice之TTS(Text-to-Speech)使用python示例
开发语言·python
Shadow(⊙o⊙)2 小时前
复习手写笔记C++继承相关1.0转录
c++
晚枫歌F3 小时前
dpdk实现arp协议,icmp协议-----架构优化
开发语言·javascript·架构
Shadow(⊙o⊙)3 小时前
复习手写笔记C++ 析构函数this指针虚表 转录
c++
Web3&Basketball3 小时前
昇腾950DT迁移实测:3笔成本账与决策树
服务器·算法·决策树
G018_star sky♬3 小时前
为什么java新建一个对象,需要obj o = new obj()这种写法
java·开发语言
星恒讯工业路由器3 小时前
工业网络设备选型:导轨式、机架式与车载式的部署差异
开发语言·网络·数据库·php·工业路由器·导轨式·车载式