zoukankan      html  css  js  c++  java
  • map和vector的迭代器失效问题

    当删除一个STL容器(比如map, vector)中的某个元素时, 会引起迭代器失效, 所以, 我们务必提高警惕。 

    题目: 删除map<int, int>中value为5的倍数的元素。 该题看起来很自然很简单, 实则有迭代器失效的陷阱。

    如果对迭代器失效问题一无所知, 则很容易写出如下的错误代码:

     1 #include <iostream>
     2 #include <map>
     3 using namespace std;
     4 
     5 typedef map<int, int> Map;
     6 typedef map<int, int>::iterator MapIt;
     7 
     8 void print(Map &m)
     9 {
    10     MapIt it;
    11     for(it = m.begin(); it != m.end(); it++)
    12     {
    13         cout << it->second << " ";
    14     }
    15 
    16     cout << endl;
    17 }
    18 
    19 void deleteValueFromMap(Map &m, int n = 5)
    20 {
    21     MapIt it;
    22     for(it = m.begin(); it != m.end(); it++)
    23     {
    24         if(0 == it->second % n)
    25         {
    26             m.erase(it);
    27         }
    28     }
    29 }
    30 
    31 int main()
    32 {
    33     Map m;
    34     int i = 0;
    35     for(i = 0; i < 21; i++)
    36     {
    37         m[i] = i;
    38     }
    39 
    40     print(m);
    41 
    42     deleteValueFromMap(m); // 程序崩溃
    43 
    44     return 0;
    45 }

          运行上述程序, 结果程序崩溃,什么原因呢? 原来, 对于关联的容器map来说, m.erase(it);后, it就失效了, 而for循环中有it++, 自然而然会出问题啊。 那怎么办呢? 且看:

      1 #include <iostream>
      2 #include <map>
      3 using namespace std;
      4 
      5 typedef map<int, int> Map;
      6 typedef map<int, int>::iterator MapIt;
      7 
      8 void print(Map &m)
      9 {
     10     MapIt it;
     11     for(it = m.begin(); it != m.end(); it++)
     12     {
     13         cout << it->second << " ";
     14     }
     15 
     16     cout << endl;
     17 }
     18 
     19 void deleteValueFromMap(Map &m, int n = 5)
     20 {
     21     MapIt it;
     22     MapIt tmp;
     23     for(it = m.begin(); it != m.end(); /*不能再自增了*/)
     24     {
     25         if(0 == it->second % n)
     26         {
     27             tmp = it++;
     28             m.erase(tmp);
     29         }
     30         else
     31         {
     32             it++;
     33         }
     34     }
     35 }
     36 
     37 int main()
     38 {
     39     Map m;
     40     int i = 0;
     41     for(i = 0; i < 21; i++)
     42     {
     43         m[i] = i;
     44     }
     45 
     46     print(m);
     47 
     48     deleteValueFromMap(m); // 程序ok
     49     print(m);
     50 
     51     return 0;
     52 }
     53        结果为:
     54 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
     55 1 2 3 4 6 7 8 9 11 12 13 14 16 17 18 19
     56 
     57  
     58 
     59  
     60 
     61        当然, 上述程序也可以继续简化为:
     62 
     63  
     64 
     65 #include <iostream>
     66 #include <map>
     67 using namespace std;
     68 
     69 typedef map<int, int> Map;
     70 typedef map<int, int>::iterator MapIt;
     71 
     72 void print(Map &m)
     73 {
     74     MapIt it;
     75     for(it = m.begin(); it != m.end(); it++)
     76     {
     77         cout << it->second << " ";
     78     }
     79 
     80     cout << endl;
     81 }
     82 
     83 void deleteValueFromMap(Map &m, int n = 5)
     84 {
     85     MapIt it;
     86     for(it = m.begin(); it != m.end(); /*不能再自增了*/)
     87     {
     88         if(0 == it->second % n)
     89         {
     90             m.erase(it++);
     91         }
     92         else
     93         {
     94             it++;
     95         }
     96     }
     97 }
     98 
     99 int main()
    100 {
    101     Map m;
    102     int i = 0;
    103     for(i = 0; i < 21; i++)
    104     {
    105         m[i] = i;
    106     }
    107 
    108     print(m);
    109 
    110     deleteValueFromMap(m); // 程序ok
    111     print(m);
    112 
    113     return 0;
    114 }
    115        结果ok.

          有的朋友肯定会问, m.erase(it++);就不会产生迭代器失效么? 确实不会! 为什么呢? 这样从it++说起, 为了简便起见, 我们用p++来代替吧。 看程序:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 int main()
     5 {
     6     char szTest[] = "abcdefg";
     7     char *p = szTest;
     8 
     9     cout << *p++ << endl;
    10 
    11     return 0;
    12 }

           大家都知道, 结果为a.  但是, 很多人错误地以为是先执行*p, 然后执行p++, 其实, 这是个天大的误解。 大家可以查一下*和++的执行顺序, 虽然*和++的优先级相同, 但此处采取的是右结合方式, 实际上先执行的是p++, 不过, p++的返回值是原来的p, 也就是说, *p++的值还是a. 

           所以, 在m.erase(it++);中,it++先执行, 此时还没有erase, 程序自然不会崩溃. 当it++执行完后, 已经指向了下一个元素了, 但it++的返回值还是当前元素, 此时再删除它, 合情合理。

            OK,  map的迭代器失效问题,先介绍到这里。

            那vector又是怎样的现象呢? 看程序:

      1 #include <iostream>
      2 #include <vector>
      3 using namespace std;
      4 
      5 typedef vector<int> Vec;
      6 typedef vector<int>::iterator VecIt;
      7 
      8 void print(Vec &v)
      9 {
     10     VecIt it;
     11     for(it = v.begin(); it != v.end(); it++)
     12     {
     13         cout << *it << " ";
     14     }
     15 
     16     cout << endl;
     17 }
     18 
     19 void deleteValueFromVector(Vec &v, int n = 5)
     20 {
     21     VecIt it;
     22     for(it = v.begin(); it != v.end(); it++)
     23     {
     24         if(0 == *it % n)
     25         {
     26             v.erase(it);
     27         }
     28     }
     29 }
     30 
     31 int main()
     32 {
     33     Vec v;
     34     int i = 0;
     35     for(i = 0; i < 21; i++)
     36     {
     37         v.push_back(i); // 不能再傻傻地用下标了
     38     }
     39 
     40     print(v);
     41 
     42     deleteValueFromVector(v); // 程序崩溃
     43     print(v);
     44 
     45     return 0;
     46 }
     47        运行程序, 结果程序也崩溃, 可见, vector也存在迭代器失效的问题, 怎么改呢? 如下:
     48 
     49  
     50 
     51  
     52 
     53 #include <iostream>
     54 #include <vector>
     55 using namespace std;
     56 
     57 typedef vector<int> Vec;
     58 typedef vector<int>::iterator VecIt;
     59 
     60 void print(Vec &v)
     61 {
     62     VecIt it;
     63     for(it = v.begin(); it != v.end(); it++)
     64     {
     65         cout << *it << " ";
     66     }
     67 
     68     cout << endl;
     69 }
     70 
     71 void deleteValueFromVector(Vec &v, int n = 5)
     72 {
     73     VecIt it;
     74     for(it = v.begin(); it != v.end(); /*不能再自增了*/)
     75     {
     76         if(0 == *it % n)
     77         {
     78             v.erase(it); // vector元素自动向前挪动了(关联的map容器元素不会自动挪动), 所以不能再把it进行++了
     79         }
     80         else
     81         {
     82             it++;
     83         }
     84     }
     85 }
     86 
     87 int main()
     88 {
     89     Vec v;
     90     int i = 0;
     91     for(i = 0; i < 21; i++)
     92     {
     93         v.push_back(i); // 不能再傻傻地用下标了
     94     }
     95 
     96     print(v);
     97 
     98     deleteValueFromVector(v); // 程序ok
     99     print(v);
    100 
    101     return 0;
    102 }
    103         结果为:
    104 
    105  
    106 
    107 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    108 1 2 3 4 6 7 8 9 11 12 13 14 16 17 18 19
    109 
    110         可见, vector迭代器失效的问题也不容忽视。

            总之, 下次说到迭代器失效(尤其涉及到容器大小的改变, 比如删除, 插入)时, 一定要留个心眼。  在实际编程中, 如果稍不注意, 则可能引起非常难以捕捉的bug, 折磨你我, 何不提前防范呢?

  • 相关阅读:
    如何查看openssl支持的所有TLS/SSL版本
    讲故事,学(AHK)设计模式—观察者模式
    React Hooks 详解 【近 1W 字】+ 项目实战
    为什么要在函数组件中使用React.memo?
    js防抖函数
    JS 深度优先遍历与广度优先遍历 实现查找
    你不知道的 requestIdleCallback
    RE:ゼロから始める文化課生活
    开学考小记 & 新生活的开始
    JS中:数组和对象的区别,以及遍历数组和遍历对象的区别
  • 原文地址:https://www.cnblogs.com/yuanshuang/p/5777905.html
Copyright © 2011-2022 走看看