前言
我们在之前的学习中已经实现过list和vector的迭代器,那么在面试中经常会有面试官问到有关于迭代器的失效问题,那么为什么迭代器会失效呢?
原因
随着VS版本的迭代,g++版本的迭代,C++标准库容器以及迭代器的源码都有比较大的修改,但是迭代器失效的问题本质归纳起来就两点:
- 不同容器的迭代器是不能进行比较的
- 容器的元素进行
insert 、erase 操作后,当前位置到末尾的迭代器 就全部失效了
注意 :当容器调用erase或insert方法后,当前位置到容器末尾元素的所有迭代器全部失效 ,或者扩容操作,申请新空间,迭代器都指向老内存,原来所有迭代器全部失效
因为对于序列式容器(如vector,deque),序列式容器就是数组式容器,删除当前的iterator会使后面所有元素的iterator都失效 。这是因为vector,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置 。所以不能使用erase(it++)的方式(会指向未知的内存),还好erase方法可以返回下一个有效的iterator。
我们使用STL的vector,模拟一下:
程序运行后,两次终端显示的程序返回码并不是0,而是-572378类似的数字,因此程序并没有正常运行。
那么,遇到这样的问题应该如何解决呢?
答:对插入,删除点的迭代器进行更新 操作。 但其实erase方法可以返回当前位置的新的有效的iterator。
于是修改代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> vec;
for (int i = 0;i < 20;++i)
{
vec.push_back(rand() % 100 + 1);
}
for (int it : vec)
{
cout << it << " ";
}
cout << endl;
auto it = vec.begin();
while(it != vec.end())
{
if(*it % 2 == 0)
{
it = vec.erase(it);
}
else
{
++it;
}
}
for (;it != vec.end();++it)
{
if (*it % 2 == 0)
{
it = vec.insert(it, *it - 1);
++it;
}
}
for (int it : vec)
{
cout << it << " ";
}
cout << endl;
return 0;
}
因此我们在使用迭代器时,如果对容器内部进行插入和删除操作时,我们一定要记得对迭代器进行更新。 学会了解决的方法,我们再来深究一下其底层实现的原理:以insert为例,以自定义vector 为例,看一下它的实现方式:
iterator insert(iterator it,const T &val)
{
verify(it._ptr - 1, _last);
T* p = it._ptr;
while (p < _last - 1)
{
_allocator.destroy(p);
_allocator.construct(p, *(p - 1));
p++;
}
_allocator.destroy(p);
_last--;
return iterator(this, it._ptr);
}
|