
博主名称:deM0one ⭐️C++专栏: C++ ⭐️C++vector第一讲: vector第一讲
文章目录
- [手写 C++ STL:vector 源码剖析与迭代器失效实战](#手写 C++ STL:vector 源码剖析与迭代器失效实战)
-
- 一、写在前面
- [二、vector 的内存布局](#二、vector 的内存布局)
- [三、size 与 capacity 的关系](#三、size 与 capacity 的关系)
- 四、构造函数家族
- [五、reserve 的实现与深拷贝陷阱](#五、reserve 的实现与深拷贝陷阱)
- [六、insert 与迭代器失效(核心重点)](#六、insert 与迭代器失效(核心重点))
- [七、erase 与返回值](#七、erase 与返回值)
- 八、野指针与程序崩溃案例分析
- 九、综合实战
- 十、总结
手写 C++ STL:vector 源码剖析与迭代器失效实战
一、写在前面
- 为什么手写 vector:深入理解 STL 底层机制,从"会用"到"懂原理"
- 本系列基于课堂手写实现,并与 VS2019 / Linux 实际运行结果对照
二、vector 的内存布局
- 三大指针:
_start、_finish、_end_of_storagebegin()→_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 的实现与深拷贝陷阱
- 实现步骤:
n > capacity()时才扩容- 记录旧大小
old_size new T[n]申请新空间- 拷贝旧数据到新空间
delete[] _start释放旧空间- 更新
_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 之后原迭代器视为失效,不要访问
- 若一定要用,必须先更新迭代器
- 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 的迭代器失效规则有什么不同?
任何的批评与建议欢迎指出,我们共同进步!