当前位置: 首页 > news >正文

vector 底层模拟实现(上):核心机制全解析 + 迭代器失效深度剖析


🔥个人主页:K 旺仔小馒头

🍉学习方向:C/C++方向学习者

📖个人专栏:《C语言》《数据结构与算法》《C++知识分享》《C语言实战编程》《算法题从优选到贪心的解题技巧》

⭐️人生格言:‌“何时葡萄先熟透,你要静候再静候”



目录

前言:

一. 先看源码

二. 构造和析构

三. capacity、size

四. reserve

五. push_back

六. 迭代器

七. operator[]

八. empty

九. pop_back

十. insert

十一. erase

结尾:


前言:

C++ vector 作为最常用的动态数组容器,其高效性背后是精巧的底层设计。本文深入剖析 vector 的构造析构机制、容量管理策略及迭代器行为,通过源码解析揭示 reserve 扩容逻辑与 push_back 的实现细节,重点探讨 insert/erase 导致的迭代器失效问题及解决方案。理解这些底层原理,是写出高效、健壮 C++ 代码的关键。


一. 先看源码

那为什么string不看源码?因为string不在STL里面,string属于C++标准库

vector 源码的头文件

这个软件(非常的方便):看源码的时候方便跳转

源码板书:


注意:模板不能分离定义到两个文件,分离定义到两个文件会导致链接错误

二. 构造和析构

知识点:

1. using 除了可以用来展开命名空间,还可以用来重定义
2. usingtypedef 的功能在这个部分是一样的,但是它比起 typedef 在额外的有些部分还有些更强大的功能(在C++14部分会讲到)

vector.h

namespace bit
{template <class T>class vector{public://typedef T* iterator;using iterator = T*;//C++14  using除了可以用来展开命名空间,还可以用来重定义vector() //构造:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){}~vector() //析构{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}        private:iterator _start;iterator _finish;iterator _end_of_storage;};
}

三. capacity、size

vector.h

public://typedef T* iterator;using iterator = T*;//C++14size_t capacity() const{return _end_of_storage - _start;}size_t size() const{return _finish - _start;}

四. reserve

vector.h

public://typedef T* iterator;using iterator = T*;//C++14void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * sz);delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}}

五. push_back

vector.h

public://typedef T* iterator;using iterator = T*;//C++14void push_back(const T& x){//满了if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;//未初始化不能直接赋值 但是不用担心,我们没有用内存池++_finish;}

六. 迭代器

vector.h

public://typedef T* iterator;using iterator = T*;//C++14using const_iterator = const T*;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}

代码测试:

test.cpp

#include "vector.h"namespace bit
{void Print(const vector<int>& v){for (auto e : v){cout << e << " ";}cout << endl;}void test_vector1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);for (auto e : v){cout << e << " ";}cout << endl;}
}int main()
{bit::test_vector1();return 0;
}

七. operator[]

vector.h

public://typedef T* iterator;using iterator = T*;//C++14using const_iterator = const T*;T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}

代码测试:

test.cpp

#include "vector.h"namespace bit
{void Print(const vector<int>& v){for (size_t i = 0; i < v.size(); i++){//v[0]++;//这儿不可以++cout << v[i] << " ";}cout << endl;}void test_vector1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v[0]++;//这儿可以++Print(v);for (auto e : v){cout << e << " ";}cout << endl;}
}int main()
{bit::test_vector1();return 0;
}

编译:

凡是看到 没有接受const....,多半是权限放大


八. empty

vector.h

public://typedef T* iterator;using iterator = T*;//C++14using const_iterator = const T*;bool empty() const{return _start == _finish;}

九. pop_back

vector.h

public://typedef T* iterator;using iterator = T*;//C++14using const_iterator = const T*;void pop_back() //尾删{assert(!empty());--_finish;}

知识点:

1. 迭代器不一定是原生指针

2. 最底层的角度而言,始终迭代器跟指针是脱不了关系的

3. 一般情况下,插入删除可能都会导致迭代器失效(容器都会有插入删除,要小心谨慎)

4. string的insert、erase也会有迭代器失效,但是string不太关注迭代器失效的问题,因为它主要是下标来控制

总结所有容器都会面临迭代器失效的问题(注意:迭代器失效以后就不要再使用)

           那它到底失不失效,结合着使用场景,结合着底层来理解

注意:insert、erase 这两个会牵扯到迭代器失效的问题

1. 第一种最简单的pos迭代器失效是野指针

2. 迭代器失效也不一定是由于扩容导致的,也有可能是进行了erase

十. insert

vector.h

public://typedef T* iterator;using iterator = T*;//C++14using const_iterator = const T*;iterator insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);//扩容if (_finish == _end_of_storage){//insert一定要测迭代器失效的问题size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}//挪动数据iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}

代码测试:

test.cpp

namespace bit
{void Print(const vector<int>& v){for (auto e : v){cout << e << " ";}cout << endl;}void test_vector2(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//v.push_back(5);Print(v);v.insert(v.begin(), 0);Print(v);auto it = v.begin() + 3;//insert以后,it是否失效?  insert以后,it失效了,it就不能使用了v.insert(it, 30);Print(v);}
}int main()
{bit::test_vector2();return 0;
}

十一. erase

vector.h

public://typedef T* iterator;using iterator = T*;//C++14using const_iterator = const T*;	iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;}

代码测试:

test.cpp

#include "vector.h"namespace bit
{void Print(const vector<int>& v){for (auto e : v){cout << e << " ";}cout << endl;}void test_vector3(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);v.erase(v.begin());Print(v);auto it = v.begin() + 2;//it是否失效?  失效,不能访问,访问结果未定义v.erase(it);Print(v);}
}int main()
{bit::test_vector3();return 0;
}

写一段程序演示一下:(不同的平台对这个地方都不一样)

编译结果:

1. vs2022 严格检查,只要erase 以后的迭代器对象就失效了,不能访问


2. Linux下没有严格检查,也就是说有些情况对,有些情况不对,有些情况可能崩溃

归根结底,这些问题本质都是迭代器失效导致的

解决办法:erase的迭代器要重置(重新赋值)以后才能使用

test.cpp

#include "vector.h"namespace bit
{void test_vector4(){std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);//Print(v);for (auto e : v){cout << e << " ";}cout << endl;//删除所有的偶数//auto it = v.begin();//while (it != v.end())//{//	if (*it % 2 == 0)//	{//		v.erase(it);//这样写是错的//	}//	++it;//}//解决办法:erase的迭代器要重置(重新赋值)以后才能使用auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);//erase返回的是刚刚删除那个数据的下一个位置,相当于已经++过了}else{++it;}}for (auto e : v){cout << e << " ";}cout << endl;}
}int main()
{bit::test_vector4();return 0;
}

结尾:

往期精选:

《优选算法:01 双指针巧解移动零问题》

《string 类模拟实现(收尾):传统与现代写法对比及底层机制探析》

《C++ vector核心接口全解:从构造到增删查改,一篇掌握所有常用操作》

结语:vector 的灵活性源于其动态内存管理,但也暗藏迭代器失效等陷阱。本文通过底层源码与实践案例,阐明了容量扩容的核心逻辑及迭代器失效的本质原因,强调 “erase 后立即重置迭代器” 的最佳实践。其实 STL 容器的设计都有共通的逻辑,理解 vector 的实现思路,也能为后续学习 list、map 等容器打下基础。如果在实际使用中遇到新问题,欢迎在评论区交流,咱们一起把 C++ 容器玩明白~

http://www.dtcms.com/a/585259.html

相关文章:

  • mysql内置函数——了解常用的函数
  • 网站建设步骤ppt一个企业seo网站的优化流程
  • 技术演进中的开发沉思-178 JSP :前世今生(下)
  • 做网站学什么软件网页美工实例教程
  • 深入理解 Spring Boot Actuator:构建可观测性与运维友好的应用
  • 现代C++的AI革命:C++20/C++23核心特性解析与实战应用
  • 【数据结构】单链表的经典算法题
  • 网站优化要用什么软件做公司网站哪家好
  • 【DaisyUI】select 和 dropdown 怎么选择?
  • 如何进行oracle提权?
  • K8s API Server 核心解析:集群的“中枢神经”与功能全解
  • 简单两步将你的python转成exe格式
  • 混合澄清槽在氧化铜矿石湿法萃取中的应用
  • Vue3 + TypeScript学习
  • GitHub Action工作流语法
  • 动态效果网站建设技术广东省建筑工程信息网
  • cpp_list
  • rk3588上用rk_mpi_vi_test与ffmpeg实战
  • Rust 练习册 :Queen Attack与国际象棋逻辑
  • CSS学习
  • 使用V4L2工具验证RK3588平台视频设备节点数据有效性
  • Rust 练习册 :Protein Translation与生物信息学
  • 网站开发课程知识点总结图片自动生成器
  • 【STL——常用遍历与查找算法】
  • 牛客网华为在线编程题
  • 29网站建设全部400网站总机 阿里云
  • 第四章 依赖项属性
  • wpf 结合 HALCON 编程 学习知识点列表有哪些?如何学习?
  • 学习C#调用OpenXml操作word文档的基本用法(5:Style类分析-3)
  • 系统运维Day03_FTP与磁盘挂载