zoukankan      html  css  js  c++  java
  • C++ std::map::erase用法及其陷阱

    1.引入:

      STL的map中有一个erase方法用来从一个map中删除制定的节点

      eg:

    map<string,string> mapTest;  
    typedef map<string,string>::iterator ITER;  
    ITER iter=mapTest.find(key);  
    mapTest.erase(iter);

       像上面这种删除单个节点,map的行为不会出现问题,但是当在一个循环里用的时候,往往会被误用。

    2.陷阱

      eg:

    for(ITER iter=mapTest.begin();iter!=mapTest.end();++iter)  
    {  
    cout<<iter->first<<":"<<iter->second<<endl;  
    mapTest.erase(iter);  
    }  

      这是一种错误的写法,会导致程序行为不可知,原因是map是关联容器,对于关联容器来说,如果一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用,负责会导致程序无定义的行为。

    3.正确用法

      正确用法一:(C++11可以,C++98不行)

        erase() 成员函数返回下一个元素的迭代器  

    std::map<std::string, std::string >::iterator it = mapTest.begin();  
    while(it != mapTest.end())  
    {  
             if(TestVal(it->second))  
             {  
                     it = mapTest.erase(it);  
             }  
             else  
                     it++;  
    }  

      正确用法二:

      使用删除之前的迭代器定位下一个元素。STL建议的使用方式

    for(ITER iter=mapTest.begin();iter!=mapTest.end();) //注意此处不能再写iter++  
    {  
    cout<<iter->first<<":"<<iter->second<<endl;  
    mapTest.erase(iter++);  
    }  
     在这种用法中,该方法中利用了后++的特点,这个时候执行mapTest.erase(it++);这条语句分为三个过程

          1、先把it的值赋值给一个临时变量做为传递给erase的参数变量

          2、因为参数处理优先于函数调用,所以接下来执行了it++操作,也就是it现在已经指向了下一个地址。

          3、再调用erase函数,释放掉第一步中保存的要删除的it的值的临时变量所指的位置。

     4.例子 

      case1: ✅  

    #include<map>
    #include<iostream>
    int main()
    {
            std::map<int, int> map_a;
            std::map<int, int>::iterator it;
    
            for (int i = 0; i != 10; i++) {
                        map_a.insert(std::map<int, int>::value_type(i, i));
            }
    
            for (it = map_a.begin(); it != map_a.end(); it++){
                        std::cout<< it->first <<std::endl;
            //              map_a.erase(it->first);
            }
    
            return 0;
    }

    结果:

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

      case2:❌

    #include<map>
    #include<iostream>
    int main()
    {
            std::map<int, int> map_a;
            std::map<int, int>::iterator it;
    
            for (int i = 0; i != 10; i++) {
                        map_a.insert(std::map<int, int>::value_type(i, i));
            }
    
            for (it = map_a.begin(); it != map_a.end(); it++){
                        std::cout<< it->first <<std::endl;
                            map_a.erase(it->first);
            }
    
            return 0;
    }

    结果:

    0
    3

    当将map大小加到1000000的时候,出core

     5.导致程序行为不可知的原因

      5.1 list和vector的erase方法都返回一个iterator,指向被删除元素的下一个元素,唯独map,set,multimap,multiset这4个竟然返回void.

               不过在C++11中已经改正过来了,都返回一个iterator

      

     原因:迭代器++的操作是在操作它保存的节点指针,所以当这个节点被删除的时候,改节点所指向的指针是无法确定的,也就是可能还是以前的数据,也可能是已经其他毫不相关的数据了,这样的行为被叫做未定义的行为,不一定会core.

  • 相关阅读:
    raw socket
    selenium and win32api
    linux ip
    network config
    grub paramiter & menu.list
    实用命令dd
    resin or tomcat .war e.g. note
    JSP 运行
    linux command screen
    docker interact example
  • 原文地址:https://www.cnblogs.com/pannyvan/p/6233400.html
Copyright © 2011-2022 走看看