一、迭代器为什么会失效
在讲解这个问题之前,我们来模拟一些场景。
场景一:把vec容器中所有的偶数全部删除
代码实现如下:
vector<int> vec;
for (int i = 0; i < 20; i++)
{
vec.push_back(rand() % 100 + 1);
}
auto it = vec.begin();
for (; it != vec.end(); ++it)
{
if (*it % 2 == 0)
{
vec.erase(it);
break;
}
}
如果有break,运行结果如下:
表示只删除一个迭代器,没有问题
如果没有break,运行结果如下:
发现进程意外中断,进程不是正常结束运行——迭代器失效问题,第一次调用erase过后,迭代器it就失效了。
因为,对一个已经失效的迭代器,再进行加加的时候代码就会产生不可预期的错误,是一个非法的操作
场景二:给vec容器中所有的偶数前面添加一个小于偶数值1的数字
代码实现如下:
vector<int> vec;
for (int i = 0; i < 20; i++)
{
vec.push_back(rand() % 100 + 1);
}
auto it = vec.begin();
for (; it != vec.end(); ++it)
{
if (*it % 2 == 0)
{
vec.insert(it,*it-1);
break;
}
}
}
有无break的效果同场景一一样。
通过以上两个场景的分析,我们可以总结出迭代器失效原因:
-
.当容器调用erase方法后,当前位置到容器末尾元素的所有迭代器全部失效,如下图:
-
当容器调用insert方法后,当前位置到容器末尾元素的所有迭代器全部失效,如下图:
-
insert来说,如果引起容器扩容。原来容器的所有迭代器就全部失效了,因为连内存都改变了
-
不同容器的迭代器是不能进行比较运算的
二、如何解决迭代器失效问题
解决方案是,对插入/删除点的迭代器进行更新操作,去当前元素增加一个元素,或者将当前失效的迭代器删除,将新的iterator返回
例如,上述场景一,改进如下:
vector<int> vec;
for (int i = 0; i < 20; i++)
{
vec.push_back(rand() % 100 + 1);
}
auto it = vec.begin();
while (it != vec.end())
{
if (*it % 2 == 0)
{
it = vec.erase(it);//对迭代器进行更新
}
else
{
++it;
}
}
运行结果如下:
场景二改进如下:
vector<int> vec;
for (int i = 0; i < 20; i++)
{
vec.push_back(rand() % 100 + 1);
}
auto it = vec.begin();
for (; it != vec.end(); ++it)
{
if (*it % 2 == 0)
{
it = vec.insert(it, *it - 1);
++it;
}
}
for (int v : vec)
{
cout << v << " ";
}
cout << endl;
三、剖析迭代器失效底层原理
1、成员变量vector<T, Alloc>* _pVec
首先,我们要在iterator类的private里面添加一个成员变量vector<T, Alloc>* _pVec;
他的作用是让迭代器知道当前迭代器迭代的是哪个容器对象,所以给迭代器提供一个指向当前容器对象的指针
有了该指针过后,我们就可以去对!=,++和*运算符进行重载了。
(1)比较运算符重载
检查迭代器的有效性,如果为空,则代表该指针指向的容器是空的表示迭代器失效了。
还有两个不同对象的迭代器比较是没有意义的
bool operator!=