编写改变容器的循环程序
添加/删除vector、string或deque元素的循环程序必须考虑迭代器、引用和指针可能失效的问题。程序必须保证每个循环步中都更新迭代器、引用或指针。如果循环中调用的是insert或erase,那么更新迭代器很容易。这些函数都会返回一个有效的迭代器,使用其可以对迭代器进行更新。
//删除偶数元素,复制奇数元素
vector<int> v = { 0,1,2,3,4,5,6,7,8,9 };
auto it = v.begin();
while (it != v.end()) {
if (*it % 2) {
it = v.insert(it, *it);//复制当前元素
it += 2;//向前移动迭代器,跳过当前元素和插入到它之前的元素
}
else {
it = v.erase(it);//删除元素,erase返回删除元素之后的元素
}
}
对于上述程序,调用erase后,不必递增迭代器,因为erase返回的是指向下一个元素的迭代器。调用insert后,insert返回指向新插入元素的迭代器。
不要保存end返回的迭代器
当添加/删除vector或string的元素后,或是在deque首元素外的任何位置添加/删除元素后,end返回的迭代器总会失效。因此,添加或删除元素的循环程序必须反复调用end()而不能在循环前将end返回的迭代器保存并一直当作容器末尾使用。
例如,如下程序希望实现功能:在容器中的每个元素后添加一个新元素。我们希望循环能够跳过新添加的元素,只处理原有元素。在每步循环后,定位迭代器使其指向下一个原有元素。如果我们试图在循环前保存end返回的迭代器,就会导致灾难:
auto begin = v.begin();
auto end = v.end();
while (begin != end) {
++begin;//向前移动begin,以在此元素后插入元素
begin = v.insert(begin, 42);
++begin;//跳过刚刚插入的元素
}
此代码行为是未定义的。在VS2019中程序检测到迭代器不匹配而终止。
必须在每次插入操作后重新调用end(),而不能在循环前保存它返回的迭代器
//while (begin != end) {
while (begin != end.()) {
++begin;//向前移动begin,以在此元素后插入元素
begin = v.insert(begin, 42);
++begin;//跳过刚刚插入的元素
}