程序中有一段代码是关于stl循环删除的内容,大体逻辑如下(实际逻辑要复杂的多,不适合用std::remove系列函数):
int main(int argc, char **argv)
{
std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it_vec = vec.begin();
while (it_vec != vec.end()) {
if (*it_vec % 2 == 0) {
vec.erase(it_vec++);
continue;
}
++it_vec;
}
return 0;
}
尽管平常都是用std::remove系列函数,但印象中上面的代码也没有任何问题的,但是程序一运行却当掉了。原因是每次执行std::vector::erase之后,迭代器是失效,当再次执行it_vec != vc.end()时,程序当掉。具体原因:std::vector,std::string,std::deque等顺序存储的容器,插入删除操作均会是所有容器失效;std::list,std::map,std::set等非顺序存储的容器,执行删除操作仅会是当前迭代器失效,插入操作不会是任何迭代器失效。无论是顺序结构容器,还是非顺序结构容器,删除操作都会返回最后一个被删除元素的后一个迭代器。
因此,对于std::vector,std::string,std::deque而言,正确的循环删除操作如下:
int main(int argc, char **argv)
{
std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it_vec = vec.begin();
while (it_vec != vec.end()) {
if (*it_vec % 2 == 0) {
it_vec = vec.erase(it_vec);
continue;
}
++it_vec;
}
std::string str("school college");
auto it_str = str.begin();
while (it_str != str.end()) {
if (*it_str == 'o') {
it_str = str.erase(it_str);
continue;
}
++it_str;
}
std::deque<int> deq{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it_deq = deq.begin();
while (it_deq != deq.end()) {
if (*it_deq % 2 == 0) {
it_deq = deq.erase(it_deq);
continue;
}
++it_deq;
}
getchar();
return 0;
}
对于std::list,std::map,std::set等非顺序容器,正确的循环删除操作可以是下面的样子:
int main(int argc, char **argv)
{
std::list<int> lst{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it_lst = lst.begin();
while (it_lst != lst.end()) {
if (*it_lst % 2 == 0) {
it_lst = lst.erase(it_lst);
continue;
}
++it_lst;
}
std::map<int, char> mp{ { 0, '0' }, { 1, '1' }, { 2, '2' }, { 3, '3' }, { 4, '4' }, { 5, '5' }, { 6, '6' }, { 7, '7' }, { 8, '8' }, { 9, '9' } };
auto it_mp = mp.begin();
while (it_mp != mp.end()) {
if (it_mp->first % 2 == 0) {
it_mp = mp.erase(it_mp);
continue;
}
++it_mp;
}
std::set<int> st{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it_st = st.begin();
while (it_st != st.end()) {
if (*it_st % 2 == 0) {
it_st = st.erase(it_st);
continue;
}
++it_st;
}
getchar();
return 0;
}
也可以是下面的样子:
int main(int argc, char **argv)
{
std::list<int> lst{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it_lst = lst.begin();
while (it_lst != lst.end()) {
if (*it_lst % 2 == 0) {
lst.erase(it_lst++);
continue;
}
++it_lst;
}
std::map<int, char> mp{ { 0, '0' }, { 1, '1' }, { 2, '2' }, { 3, '3' }, { 4, '4' }, { 5, '5' }, { 6, '6' }, { 7, '7' }, { 8, '8' }, { 9, '9' } };
auto it_mp = mp.begin();
while (it_mp != mp.end()) {
if (it_mp->first % 2 == 0) {
mp.erase(it_mp++);
continue;
}
++it_mp;
}
std::set<int> st{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it_st = st.begin();
while (it_st != st.end()) {
if (*it_st % 2 == 0) {
st.erase(it_st++);
continue;
}
++it_st;
}
getchar();
return 0;
}