C++--vector第二讲:手写 C++ STL:vector 源码剖析与迭代器失效分析


博主名称:deM0one ⭐️C++专栏: C++ ⭐️C++vector第一讲: vector第一讲

文章目录

  • [手写 C++ STL:vector 源码剖析与迭代器失效实战](#手写 C++ STL:vector 源码剖析与迭代器失效实战)

手写 C++ STL:vector 源码剖析与迭代器失效实战

一、写在前面

  • 为什么手写 vector:深入理解 STL 底层机制,从"会用"到"懂原理"
  • 本系列基于课堂手写实现,并与 VS2019 / Linux 实际运行结果对照

二、vector 的内存布局

  • 三大指针:_start、_finish、_end_of_storage
    • begin() → _start(首元素)
    • end() → _finish(最后一个元素的下一个位置)
    • capacity() → _end_of_storage(容量边界)
  • size() 与 capacity() 的区别
  • reserve(capacity()) 的用途:确保不扩容时空间充足

三、size 与 capacity 的关系

  • 三种情形:
    • n < size:不允许缩小容量
    • size < n < capacity:容量已够,无需扩容
    • n > capacity:触发真正的扩容

四、构造函数家族

  • vector(size_t n):构造 n 个默认元素
  • vector(size_t n, const T& val):构造 n 个 val
  • 区间构造函数(重点)
    • template <class InputIterator> vector(InputIterator first, InputIterator last)
    • 可用任意容器的迭代器区间初始化(如 list 的迭代器构造 vector)
    • 要求迭代器类型与元素类型匹配
  • 知识点:类模板的成员函数还可以是函数模板
    • <T> 只是给 IntelliSense 看的示例模板参数

五、reserve 的实现与深拷贝陷阱

  • 实现步骤:
    1. n > capacity() 时才扩容
    2. 记录旧大小 old_size
    3. new T[n] 申请新空间
    4. 拷贝旧数据到新空间
    5. delete[] _start 释放旧空间
    6. 更新 _start、_finish、_end_of_storage
  • 坑点 1:用 memcpy 是浅拷贝
    • 对 vector<string>、vector<vector<int>> 等非 POD 类型,memcpy 会复制指针而非内容
    • 调试器中出现"读取字符串字符时出错",旧内存释放后成为野指针/悬垂指针
  • 正确做法:用 for 循环逐元素赋值 tmp[i] = start[i](深拷贝语义)

六、insert 与迭代器失效(核心重点)

  • iterator insert(iterator pos, const T& x) 的流程
    • 从 end() 开始倒着向后搬移元素,为插入位置腾出空间
    • 插入新元素后更新 _finish
  • 迭代器失效问题
    • insert 后数据发生挪动,原迭代器 p 指向的元素已改变("位置意义已经变了")
    • 结论:insert 之后原迭代器视为失效,不要访问
    • 若一定要用,必须先更新迭代器
  • 失效后的真实表现
    • VS2019 强制检查:访问失效迭代器直接报错
    • 程序退出码 -1073741819(即 0xC0000005,访问冲突)

七、erase 与返回值

  • iterator erase(iterator position) 的实现
    • 断言保护:POS < _finish(如 vector.h 第 110 行 Assertion failed)
    • 删除后向前搬移后续元素
  • 返回值语义(官方说明)
    • 返回被删除元素的后一个元素的迭代器
    • 若删除的是最后一个元素,返回 end()

八、野指针与程序崩溃案例分析

  • 案例:insert 后继续使用旧迭代器 → 野指针 → 崩溃
  • 对比 VS2019 与 Linux 下 ./a.out 的不同表现(强制检查 vs 静默出错)
  • 调试技巧:通过退出码与断言信息定位问题

九、综合实战

  • 编写 print_container 模板打印任意容器
    • const vector<T>& 传参 + auto& e 遍历
  • 多种初始化方式对比
    • 直接构造、区间构造、v1.begin() + 3 子区间构造
    • vector<int> v2(v1.begin(), v1.end())
    • vector<int> v3(lt.begin(), lt.end())(list 迭代器初始化)

十、总结

  • vector 三指针结构一句话回顾
  • 扩容 = 申请 + 拷贝 + 释放 + 更新指针
  • 迭代器失效的黄金法则:增删元素后不要继续使用旧迭代器
  • 延伸思考:list / deque 的迭代器失效规则有什么不同?

任何的批评与建议欢迎指出,我们共同进步!

相关推荐
邪修king2 小时前
Re:Linux 系统篇(三十五):进程间通信Chapter2:从匿名管道到进程池:Linux 进程间通信实战(深度理解「先描述,再组织」)
java·linux·运维
拆房老料2 小时前
【第四部分:Agentic 智能体设计模式】16. 一个 Prompt 搞不定复杂任务?试试 Prompt Chaining
java·前端·人工智能·设计模式·prompt
张张123y3 小时前
后端第十四课:Agent 记忆系统(让 Agent 记住上下文和用户,跨会话不健忘)
java·大数据·人工智能·python·agent·记忆
曹牧7 小时前
C#:空合并运算符
java·前端·数据库
布吉岛的石头8 小时前
Java 程序员第 50 阶段2:预训练的目标函数:自回归语言建模本质
java·开发语言·人工智能·深度学习·回归·transformer
golang学习记8 小时前
IDEA 2026年的玩法已经完全不同
java·ide·intellij-idea
紧跟先前的步伐8 小时前
Go AI Agent 实战 05|Temperature 与生成参数:让模型输出可控
java·人工智能·golang
金銀銅鐵9 小时前
[Java] 展示添加元素时 ArrayList 的内部变化
java·后端·ai编程
斌糖雪梨9 小时前
spring auth 核心流程详解
java·后端·spring
芝麻别开门10 小时前
gst-shark安卓编译使用方法
android·gstreamer·gst-shark