• 在遍历中使用 iterator/reverse_iterator 进行 Erase 的使用方法


    在遍历中使用 iterator/reverse_iterator 进行 Erase 的使用方法
    本文遵循“署名-非商业用途-保持一致”创作公用协议

    众所周知,在使用迭代器遍历 STL 容器时,须要特别留意是否在循环中改动了迭代器而导致迭代器失效的情形。以下我来总结一下在对各种容器进行正向和反向遍历过程中删除元素时,正确更新迭代器的使用方法。本文源代码:https://code.csdn.net/snippets/173595

    首先,要明确使用正向迭代器(iterator)进行反向遍历是错误的使用方法,要不干嘛要有反向迭代器呢(reverse_iterator)。其次,依据容器的特性,遍历删除操作的使用方法能够分为两组,第一组是 list 和 vector,第二组是 map 和 set。


    接下来,看看详细怎么个使用方法。

    第一种情形:正向遍历删除元素

    对 list 和 vector 来说,它们的 erase 函数会返回下一个迭代器,因此在遍历时,仅仅须要 it = c.erase(it); 就可以。

    对 map 和 set 来说,它们的 erase 函数返回的 void,而在进行 erase 之后,当前迭代器会失效,无法再用于获取下一个迭代器。因此须要 erase 之前就获取指向下一个元素的迭代器。如: 

    tmpIt = it;
    ++it;
    c.erase(tmpIt);
    利用后缀++操作符的特性(先创建副本,然后再递增迭代器,然后返回副本)上面的三行代码能够简化为一行:
    c.erase(it++);


    list 正向遍历删除元素演示样例(vector 使用方法同样):

        list<int>::iterator it;
        for (it = l.begin(); it != l.end();)
        {
            if (0 == (*it) % 2) {
                it = l.erase(it);
            }
            else {
                ++it;
            }
        }

    map 正向遍历删除元素演示样例(set 使用方法同样)

        map<int, int>::iterator mit;
        for (mit = m.begin(); mit != m.end();)
        {
            if (0 == mit->first % 2) {
                m.erase(mit++);
            }
            else {
                ++mit;
            }
        }

    另外一种情形,反向遍历删除元素

    关于正向/反向迭代器的关系,请參考《Effective STL》,在这里我仅仅说明一点,两者相差一个元素,从一个反向迭代器获得相应的正向迭代器须要使用 base() 方法。例如以下图所看到的:ri 是指向元素3的反向迭代器,而 i 是 ri.base() 所得到的正想迭代器。


    由于全部的 erase 函数都仅仅接受正向迭代器 iterator,所以在进行反向遍历删除元素时,首先须要将 reverse_iterator 转换为 iterator,然后再考虑更新迭代器的问题。

    先来分析怎样将 reverse_iterator 转换为 iterator。如上图所看到的,我们想要删除元素3,而 ri.base() 所得到的正向迭代器 i 指向的事实上 4 了,因而为了正确地删除元素 3,须要将ri往前(反向的)挪一个位置。也就是说,这一步的删除使用方法应为:

    c.erase((++rit).base());
    

    或:(想想为什么?,但这个使用方法不具备可移植性,由于有些 STL 实现不同意改动函数返回的指针)

    c.erase(--(rit.base();


    然后,我们来分析迭代器更新的问题。
    对 list/vector 来说,由于的 erase 能够返回一个有效的正向迭代器,因而仅仅须要将返回的正向迭代器转换为反向迭代器就可以。

    对 map/set 来说,由于在进行删除操作 l.erase((++rit).base()) 时,迭代器已经更新过了,真是一举两得啊。从这里也能够看出,使用这样的先递增后 base() 的转换删除法,代码更清晰。

    至此,理论分析完成,以下我们来看详细的实例。

    list 反向遍历删除元素演示样例(vector 使用方法同样):

        // erase with reverse_iterator
        list<int>::reverse_iterator rit;
        for (rit = l.rbegin(); rit != l.rend();)
        {
            if (0 == (*rit) % 2) {
                rit = list<int>::reverse_iterator(l.erase((++rit).base()));
            }
            else {
                ++rit;
            }
        }

    map 反向遍历删除元素演示样例(set 使用方法同样):

        // erase with reverse_iterator
        map<int, int>::reverse_iterator rit;
        for (rit = m.rbegin(); rit != m.rend();)
        {
            if (0 == rit->first % 2) {
                m.erase((++rit).base());
            }
            else {
                ++rit;
            }
        }

    OK,删除使用方法相信大家都明确了,可是,可是,引起迭代器失效的操作还有插入操作呀,相信聪明的你一定能够举一反三正确更新迭代器~~


  • 相关阅读:
    自建 IPA 分发平台
    一个优雅的占位图解决方案。适用于 UITableView 和 UICollectionView。
    Vuejs2.0购物车和地址选配学习笔记
    UIWebView 加 MJRefresh 同时解决底部黑影问题
    UIWebView 不自动全屏播放视频
    左右分页按钮的集合视图控件。用于快速编写出集合视图上分页多按钮点击事件!
    课程总结
    IO流实训
    事件处理
    变色
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4279187.html
Copyright © 2020-2023  润新知