当前位置:网站首页>《STL容器篇》-Vector模拟实现
《STL容器篇》-Vector模拟实现
2022-07-22 19:13:00 【李逢溪】
一、本篇接口实现介绍
iterator begin()
const_iterator begin()const
iterator end()
const_iterator end()const
vector()
template<class InputIterator>
vector(InputIterator first, InputIterator last)
vector(const vector<T>& v)//传统写法
vector(const vector<T>& v)//现代写法
void swap(vector<T>& v)
~vector()
vector<T>& operator=(const vector<T>& v)//传统写法
vector<T>& operator=(vector<T> v)//现代写法
void reserve(size_t n)
void resize(size_t n, const T& x = T())
void push_back(const T& x)
T& operator[](size_t pos)
const T& operator[](size_t pos)const
size_t capacity()const
size_t size()const
bool empty()const
void clear()
iterator insert(iterator pos, const T& x)
iterator earse(iterator pos)本篇不为造更好的轮子,只为让我们了解一点底层实现,更好的掌握vector的使用,上述接口有许多和string篇的相似,因此,这里只挑选部分值得我们注意的点进行解析。
二、接口全实现
https://gitee.com/zxlfx/c-code-warehouse/tree/master/2022_7_20/2022_7_20
三、部分接口解析
3.1插入数据后,迭代器会失效
iterator insert(iterator pos, const T& x) { assert(pos>=_start && pos <= _finish); if (_finish == _end_of_storage) { size_t n = pos - _start; reserve(capacity() == 0 ? 4 : 2 * capacity()); pos = _start + n; } T* end = _finish - 1; T* start = pos; while (end >= start) { *(end + 1) = *end; end--; } *pos = x; _finish++; return pos; }对于vector来说,在pos位置之前要插入数据,就得不断挪动数据,对于vector的扩容,原空间会被销毁,同时将原空间的数据拷贝至新空间,如果扩容了,那么pos还会指向原空间,此时pos就是野指针,属于迭代器失效的一种情况,解决办法,通过返回新空间的pos让原pos接受。第二种,如果没有扩容,举个例子,vector中有1 、2、3、4,pos指向3,在3前面插入30之后,pos还是会指向原空间,此时pos指向的是30,它指向的内容变了,属于意义变了,这是迭代器失效的第二种情况。
~提问环节~
如果让你在偶数的前面插入该偶数*10的数字,以下写法正确吗?
void test() { vector<int> v{ 1,2,3,4,5,6 }; auto it = v.begin(); while(it!=v.end()) { if (*it % 2 == 0) { v.insert(it, *it * 10); } it++; } for (auto e : v) { cout << e << " "; } cout << endl; }在调用test()之后,程序崩溃了?这是为什么呢?
跟随程序的执行流可知,当it指向2时,需要插入20,那么将挪动数据2、3、4、5、6,让20插入到2原来的位置,然后++it,++it之后还是指向2,将陷入无穷的循环,导致程序崩溃。
这属于迭代器的意义变了,如果出现扩容,it同时也会变成野指针(指向原空间),解决办法是:利用insert的返回值,该返回值指向的是最新插入元素的位置。
更改代码后:
void test() { vector<int> v{ 1,2,3,4,5,6 }; auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { it = v.insert(it, (*it) * 10); it += 2; } else { it++; } } for (auto e : v) { cout << e << " "; } cout << endl; }这里用it = v.insert(it, (*it) * 10)。可以避免it是野指针的问题,同时it+=2,可以跳至该偶数的下一个数。
同理,erase也会出现迭代器失效的问题,因为erase删除数据也是通过挪动数据实现的。
~提问环节~
vector中所有的偶数,以下代码正确吗?
void test() { vector<int> v{ 1,2,3,4,5,6 }; auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { v.erase(it); } it++; } }乍一看,这代码好像没什么问题,遍历一遍vector即可,如果是偶数就删除,不是偶数就++,逻辑似乎很正确,但站在erase实现的角度来看,这里会出现迭代器失效的问题。
因为erase删除数据是通过挪动数据覆盖实现的,如果it指向2,那么删除2之后,it指向的就是3了,然后++,那么3就没判断了,而是it直接指向了4,然后删除4之后,it指向5,++之后,it指向6,删除6,it等于end(),++之后,it指向end()之后的值,永远不可能与end()的相等了,陷入死循环。
如果vector为1、2、3、4、5,那么程序又能删除偶数了。
这里我改使用gcc编译器,因为vs2019能够检测到erase迭代器失效的问题。
但是我们都知道,在删除2之后,it指向3,之后++it,那么就会跳过3的判断,如果这里把3改为2,vector为1 2 2 4 5,那么我们可预测结果会为1、2、5.。
如何解决迭代器失效呢?利用erase的返回值,同样的,erase的返回值指向的是原删除元素的下一个位置。
修改代码后:
void test() { vector<int> v{ 1,2,2,4,5,6 }; auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { it = v.erase(it); } else { it++; } } for (auto e : v) { cout << e << " "; } cout << endl; }
有的人可能有疑惑,返回值好像并没有什么用呀,因为it还是指向原位置呀,是的it还是指向原位置,但保不齐调用erase之后发生了缩容,这是erase的实现者决定的,如果发生了缩容,那么it就是野指针,接受erase的返回值可避免野指针的出现。经过实验,vs2019、g++的erase都没有采取缩容,因为缩容是一种时间换空间的做法,对于当代计算机而言,空间不是特别的缺(根据摩尔定律)。
对于vector,值得探究的还有个反向迭代器的适配(涉及迭代器萃取、模板的特化),这里我准备在list中引入,如有错误或者疑惑的地方,欢迎读者与我交流~。
边栏推荐
- Alibaba cloud disk IOS / Android version 3.8.0 update, video can be cached according to the definition
- 7.20 codeforces round 763 (Div. 2) C (two points) d (mathematical expectation) Backpack + tree DP review
- TCP waves four times
- mysql约束之_外键约束 foreign key
- 暑假笔记1
- ABAP ALV步骤
- Win10安装QT(在线安装包)闪退(Crash)的问题与解决
- OSPF related content
- 7.20 Codeforces Round #763 (Div. 2) C(二分) D(数学期望)背包+树形dp复习
- 常用正则表达式最强整理速查手册(荣耀典藏版)
猜你喜欢

Feign remote call lost request header problem solution

时代潮头,华为将风帆对准数字金融的风与海
![swing-[MyNote]-实现像IDEA一样的定位scroll from souce功能](/img/ee/53aae922d7a4b3df3871a3e997cc57.png)
swing-[MyNote]-实现像IDEA一样的定位scroll from souce功能

Dao smart contract DAPP system development technology

OWA邮件系统登录双因子认证(短信认证)方案
![[machine learning basics] unsupervised learning (5) -- generation model](/img/55/14c03c170ae73e121ca89b716bf9c6.png)
[machine learning basics] unsupervised learning (5) -- generation model

在 MySQL 中使用枚举的陷阱一定要注意!

Sharepreference principle + practical analysis

Urllib download (urlretrieve())

I used fluent deskstop to build a Mars xlog log parsing tool
随机推荐
ICML2022 | ROCK: 关于常识因果关系的因果推理原则
小红书携手HMS Core,畅玩高清视界,种草美好生活
第六章 更多监督训练
上交所行情文件解析之mktdt03
Feign远程调用丢失请求头问题解决
第三章 Encog Workbench
第八章 使用时序数据
阿里云云盒、专属Region首批满分通过可信云专有云综合能力评估
LSA related content in OSPF
Drawing lollipop chart with R language
Day27作业
1. Summary of strengthening learning foundation
Network diagram of R language student letter chart learning
Codeforces Round #808 (Div. 2) C,D Codeforces Round #809 (Div. 2) C
472-82(22、165、39、剑指 Offer II 078、48. 旋转图像)
Oracle switches users and queries database commands under linux environment
Apifox学习记录
2021-03-01
Unable to install schedule
暑假笔记1


