目录
本期我们主要进行vector迭代器失效问题的讨论。
迭代器失效的场景
insert插入元素时迭代器失效
先看代码:
cpp
iterator insert(iterator pos, T val)
{
assert(pos >= _start);
assert(pos <= _finish);
size_t length = pos - _start;
if (_finish == _endofstorage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
pos = _start + length;
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
_finish--;
*pos = val;
return pos;
}
insert插入元素时为什么会导致迭代器时效呢?其实这主要是在插入元素时,空间不够,扩容导致的。
**解析:**如图,假设在pos位置插入一个元素,但是此时空间不够,所以我们进行了扩容。但是扩容之后此时的pos仍然指向了之前的位置,所以此时的pos就已经失效了(这就称作迭代器失效),所以我们得通过pos-_start的值计算出来pos位置与_start的位置,然后在扩容之后,通过差值计算出来真正的pos的位置。需要注意的是,我们此时的正确位置只是形参里pos的正确位置,但是实参是没有发生变化的,因为insert形参列表中的pos位置传的是实参pos的值,所以我们得通过返回值的方式返回扩容后正确的pos位置。
erase删除元素时迭代器失效
我们要删除vector中的偶数元素,此时erase失效场景有三种:
第一种:
解析 :思想:我们要删除所有的偶数所以先让pos指向第一个元素的位置,如果不是偶数就 + +如果pos位置的元素是偶数就删除pos 位置的元素,删除元素的思想其实就是把要删除的元素的后面的所有元素统一往前移动一个位置,这样就删除了要删除的元素。对于第一种情景,我们发现,确实是删除了所有的偶数。
注意: end表示最后一个元素的位置的下一个位置。
第二种:
**解析:**跟第一种情景的思想是一样的,但是到最后会产生迭代器失效,在删除了元素之后,我们会先将指向了删除了的元素的位置的指针+ +,然后再将finish--但是这样会产生一种极端的情况,如图,就是当最后一个元素是偶数时,我们将最后一个元素删除之后,先将指向了最后一个元素的指针++,然后将finish--,这就导致了pos和 finish错位,因为循环的终止条件是pos<_finish,因为pos和 finish错位,这就导致循环不会终止,所以这种情况是有问题的。
第三种:
**解析:**这种情景我们称作连续偶数的情景,在这种情景下,同样是因为,我们再删除完了元素之后先++了pos,因为删除的本质就是将删除位置的元素后面的所有元素统一往前移动一格位置,就会导致要删除的位置的元素被删除之后,要删除的元素的下一个元素就自动的移动到了要删除的元素的位置上,但是我们仍然++pos,就会导致删除的元素的下一个元素被无意间跳过,这就导致了要删除的元素的下一个元素永远无法删除,这便是这种情景没有完全删除所有的偶数元素的原因。
在这三种情形下迭代器都已经失效了,那么我们该怎样去进行代码的修改呢?直接代码:
cpp
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos;
while (begin < _finish - 1)
{
*begin = *(begin + 1);
begin++;
}
--_finish;
//这里要返回pos位置上的数据,是因为,可能会跳过元素,这里有几种情况,所以我们必须返回删除之后,正确的迭代器的位置,所以我们要返回pos
// 1 2 3 4 5 -> 正常
// 1 2 3 4 -> 崩溃
// 1 2 4 5 -> 没删除完
return pos;
}
这是删除元素的代码,我们用迭代器作为返回值的原因是因为,要返回删除的元素的下一个元素的位置,但是因为删除的思想,会导致要删除的元素后面的所有元素统一往前移动一个位置,所以要删除的元素的下一个元素的位置其实就是pos位置。
原始版本:
cpp
void test1()
{
vector<int> v1;
v1.pushback(1);
v1.pushback(2);
v1.pushback(4);
v1.pushback(5);
vector<int>::iterator pos= v1.begin();
while (pos != v1.end())
{
if (*pos % 2 == 0)
{
v1.erase(it);
}
++pos;
}
}
每次删掉元素都要进行迭代器的++,导致迭代器失效。
改进版本:
cpp
void test1()
{
vector<int> v1;
v1.pushback(1);
v1.pushback(2);
v1.pushback(4);
v1.pushback(5);
vector<int>::iterator pos =v1.begin();
while (pos != v1.end())
{
if (*pos % 2 == 0)
{
pos = v1.erase(pos);
}
else
{
++pos;
}
}
}
}
改进了之后,再删除掉元素之后,不直接++,而是返回要删除的元素的下一个元素的位置作为pos迭代器的值(采用这种方式的原因吗,其实还要为缩容去考虑,不然完全可以删除完之后不返回任何值)。
编译器缩容场景的迭代器失效很少见到,所以我们就不做介绍。
本期内容到此结束^_^