容器的遍历在我们的代码中是经常出现的场景
for(auto iter = T.begin(); iter != T.end(); ++iter)
{
...
}
复制代码
在业务逻辑中,难免出现容器的增删改查这些操作,静态操作(改、查)由于不设涉及容器结构的变化,只是数据的变化,不会出现迭代器失效问题。只有动态操作(增、删),但增的操作一般不会持续整个遍历期间,重点介绍删操作。
erase
map
首先看一下 erase()函数原型。分为两种情况
C++98
(1) void erase (iterator position);
(2) size_type erase (const key_type& k);
(3) void erase (iterator first, iterator last);
复制代码
C++11
(1) iterator erase (const_iterator position);
(2) size_type erase (const key_type& k);
(3) iterator erase (const_iterator first, const_iterator last);
复制代码
map 执行 erase()操作后,删除的迭代器会失效,其他迭代器仍不失效。
错误写法
for(auto iter = map.begin(); iter != map.end(); ++iter)
{
if(...)
{
...
iter = map.erase(iter);
}
}
复制代码
由于代码执行 map.erase(iter)操作后,iter 已经不在红黑树里面了,执行++iter 操作无法找到它的直接后继,代码会出错。
正确写法:
for(map<T>::iterator iter = map.begin(); iter != map.end(); )
{
if(...)
{
map.erase(iter++); //C++11,C++98都支持
//iter = map.erase(iter); 仅仅支持C++11,C++98不支持
}
else
{
++iter;
}
}
复制代码
在不确定编译器是否支持 C++11 的情况下,上述写法更安全。
list
erase()函数原型
C++98
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
复制代码
C++11
iterator erase (const_iterator position);
iterator erase (const_iterator first, const_iterator last);
复制代码
list 执行 erase()操作后,删除的迭代器会失效,其他迭代器仍不失效。
可以发现函数返回值与 map 还是有所差异的
for(list<T>::iterator iter = list.begin(); iter != list.end(); )
{
if(...)
{
list.erase(iter++); //C++11,C++98都支持
//iter = list.erase(iter); //C++11,C++98都支持
}
else
{
++iter;
}
}
复制代码
上述两种写法 C++11、C++98 都是支持的
vector
C++98
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
复制代码
C++11
iterator erase (const_iterator position);
iterator erase (const_iterator first, const_iterator last);
复制代码
vector 虽然提供了 erase 操作,但是并不建议使用,erase 操作后,position 之前的迭代器不会失效,position 之后的迭代器会失效。
insert
map 和 list 的 insert 操作不会导致其他迭代器的失效,但 vector 就不一样,会导致迭代器失效,因为 insert 会产生扩容操作,扩容操作之后,所有的迭代器都会失效。vector 的 insert 接口不建议调用,本身使用概率低,但更多的应该注意 vector.push_back()操作,因为此操作同样有可能会触发扩容操作,导致之前记录的所有迭代器全部失效,需要重点关注。
评论