zoukankan      html  css  js  c++  java
  • 迭代器失效问题

    话说回来,今天 ymx 发了个关于 std::list 迭代器失效的问题,正好也让我对这方面的芝士有所了解,故作文以记之((

    首先先贴一张 C++ reference 里的图:

    我们知道 C++ STL 中的容器大致可分为四类,序列型(如 vector,deque,queue)、关联型(set,map,multiset)和链表型(list,forward_list),这三种类型的容器迭代器失效的情况是不同的,因此这里会对其一一进行阐述:

    1. 序列型容器

    序列型容器的特点是它内存分配是一段连续的区间,也就是说,当你删除一个元素后会导致后面的元素全部向前移动一格,使得后面元素的迭代器全部失效,因此删除 vector 中的元素不能这么写:

    for(vector<int>::iterator it=vec.begin();it!=vec.end();it++){
        if((*it)>100) vec.erase(it);
    }
    

    也不能这么写:

    for(vector<int>::iterator it=vec.begin();it!=vec.end();it++){
        if((*it)>100) vec.erase(it++);
    }
    

    一个解决办法是:利用 vector 类型中 erase 函数的返回值找出下一个合法的地址,即:

    for(vector<int>::iterator it=vec.begin();it!=vec.end();){
        if((*it)>100) it=vec.erase(it);
        else ++it;
    }
    

    此外,根据 vector 自动伸长的原理(如果不清楚参见 THUSC2021 试机赛 T2,如果没参加过 THUSC2021……那恐怕我也没办法了),若删除一个元素后 vector 的长度变为某个 (2^k-1(kinmathbb{Z})),那么 vector 分配的内存大小将会减半,导致 vector 内部全部重构,此时整个 vector 的迭代器都会失效。

    2. 关联型容器

    与序列型容器不同的是,关联型容器内部是二叉树或红黑树,因此它们的内存分配是没有规律的,删除一个元素后只会对使被删除的元素的迭代器失效。但是注意,失效后的迭代器你对它进行任何操作都将会导致 RE,因此你不能这样删除元素:

    for(set<int>::iterator it=st.begin();it!=st.end();it++){
        if((*it)>100) st.erase(it);
    }
    

    原因是你对失效的迭代器进行自增操作。
    而这样写就不会出现问题:

    for(set<int>::iterator it=st.begin();it!=st.end();it++){
        if((*it)>100) st.erase(it++);
    }
    

    值得注意的一点是,在 std::set 类型中,erase 函数是没有返回值的,因此也就不能通过调用 erase 函数获得下一个有效地址的位置,否则会获得 CE 的好成绩(London Fog

    3. 链表型容器

    对于链表型容器而言,虽然从外表上看起来它使用了数组型的结构,但实际上它内存的分布也是不连续的,因此删除一个元素并不会对其他元素的地址产生影响,因此可以通过 erase 函数的返回值获得下一个有效地址,也可通过在删除的同时自增迭代器的方式避免迭代器失效

  • 相关阅读:
    记一个python字典和json.dumps()的坑
    多态
    接口
    继承
    Python---进阶---文件操作---获取文件夹下所有文件的数量和大小
    Python---进阶---文件操作---按需求打印文件的内容
    Python---进阶---文件操作---比较文件不同
    Python---进阶---函数式编程---按照权重排序
    Python---进阶---函数式编程---lambda
    man clock
  • 原文地址:https://www.cnblogs.com/ET2006/p/iterator-invalidate.html
Copyright © 2011-2022 走看看