C++ 迭代器失效问题

摘要:
问题是迭代器失败=v.end();it++){cout˂˂*it˂˂“”;}}让我们看看insert操作如何使迭代器vector_insert()的inttest无效{vector<int>v;v.reserve;//注意,如果没有这句话,输出结果是999800100398。可以看出,迭代器不在我们想要的位置,因为当插入空间不足时,它会导致扩展。原始数组被移动到更大的空间,迭代程序保持在原始位置。正确的结果是321010009998v[0]=100;v[1]=99,v[2]=98; autoit=v.开始();v、 插入;v、 插入;v、 插入;v、 插入;for(autoit=v.begin();它

今天同学华为面试被问到vector有什么问题了,我一拍脑门,vector有什么问题??

原来是迭代器失效问题。先看看vector中:

void test_vector_erase(){
    vector<int> v = {1,2,3,4,5};
    for(auto it = v.begin(); it != v.end(); it++){
        if(*it == 3)
            v.erase(it);
    }
    for(int& x : v) cout<<x<<" ";
}

我本来是想以这个例子举例的,但似乎没有bug??给我整不会了。

但是我又分别测试了删除 1 和删除 5 的情况,答案是删除5的时候会出问题

void test_vector_erase(){
    vector<int> v = {1,2,3,4,5};
    for(auto it = v.begin(); it != v.end(); it++){
        if(*it == 5)       //会出问题, 这里改成*it  > 2也会出问题
            v.erase(it);
    }
    for(int& x : v) cout<<x<<" ";
}

其实erase操作之后it还是会留在原位置,比如1,2,3,我们删除了2,变成了1,3,it由原来指向变成了指向3,这个时候做--操作,抵消for循环里面的++操作可以得到正确的结果

void modify_vector_erase(){
    vector<int> v = {1,2,3,4,5};
    for(auto it = v.begin(); it != v.end(); it++){
        if(*it > 2) {
            it = v.erase(it);
            it--;
        }
    }
    for(auto it = v.begin(); it != v.end(); it++){
        cout<<*it<<" ";
    }
}

再来看看insert操作是如何让迭代器失效的

int test_vector_insert(){
    vector<int> v(3);
    v.reserve(10);  //注意这句话, 不加这句话输出的结果是99 98 0 0 100 3 98 ,可以看出迭代器并不在我们想要的位置,因为insert当空间不够时会引起扩容,原数组被搬到了另一块更大的空间,迭代器还留在原来的位置
  //加上这句话,也就是vector的预分配函数后,我们insert四个数不会引起扩容,因此会打印出正确结果 3 2 1 0 100 99 98 
v[0] = 100;v[1] = 99, v[2] = 98; auto it = v.begin(); v.insert(it, 1, 0); v.insert(it, 1, 1); v.insert(it, 1, 2); v.insert(it, 1, 3); for(auto it = v.begin(); it != v.end(); it++){ cout<<*it<<" "; } return 0; }

再看看关联性容器map, 对于map和list,这种存放位置不连续的容器,当客户端对它进行元素新增操作(insert)或者删除(erase)操作时,操作之前的所有迭代器在操作完成后都依然有效,被删除的那个元素的迭代器是个例外。

 首先直接删除任意一个元素后就不会继续遍历后面的元素了,输出1,3,4,5。被删除的it被删除后还是指向3,而此时it的下一个是mp.end()

void test_map_erase(){
    map<int, char> mp{{1,'a'},{2,'b'},{3,'c'},{4,'d'},{5,'e'}};
    for(auto it = mp.begin(); it != mp.end(); it++){
        if(it -> first > 2){
       mp.erase(it);
       cout<<it->first<<endl;
     }     }
for(auto it = mp.begin(); it != mp.end(); it++) cout<<it->first<<" "; }

下面这样可以完整删除3,4,5

void test_map_erase(){
    map<int, char> mp{{1,'a'},{2,'b'},{3,'c'},{4,'d'},{5,'e'}};
    for(auto it = mp.begin(); it != mp.end(); it++){
        if(it -> first > 2) {
            mp.erase(it++);
            it--;
        }
    }
    for(auto it = mp.begin(); it != mp.end(); it++)
        cout<<it->first<<" ";
}

总之,我觉得不会有哪个傻蛋会一遍修改容器一边遍历容器吧!

另外erase操作和remove操作也是有区别的

//remove的官方解释,是把一个范围内的某个数删除,但是并没有释放容器空间
template <class ForwardIterator, class T>
  ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val)
{
  ForwardIterator result = first;
  while (first!=last) {
    if (!(*first == val)) {
      if (result!=first)
        *result = move(*first);
      ++result;
    }
    ++first;
  }
  return result;
}

//vector 中的 erase是删除某个位置并且释放了空间的(ps:map的源码我没看到,不知道有没有释放)
iterator erase(iterator position){
    if(position + 1 != end())
        copy(position + 1, finish, position);
    --finish;    //finish是vector中已使用空间的尾,end_of_storage 是vector可使用空间的尾
    destroy(finish);
    return position;
}

STL共有这五种类型的迭代器,迭代器其实是一种行为类似智能指针的对象。

C++ 迭代器失效问题第1张       C++ 迭代器失效问题第2张

免责声明:文章转载自《C++ 迭代器失效问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇SQL Server 2008 空间数据存储摘抄(SRID 点 MultiPoint LineString MultiLineString 多边形 MultiPolygon GeometryCollection)Ubuntu不可以ping百度,但是可以ping通其ip下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

STM32CubeIDE+FreeRTOS任务管理实验

新建工程RTOS_Task,配置如下: Ctrl + S生成代码 修改代码, 1,在main.h中添加 //添加include/*Private includes ----------------------------------------------------------*/ /*USER CODE BEGIN Includes */#inclu...

Opencv2系列学习笔记2(图像的遍历)

原文地址:http://blog.csdn.net/lu597203933/article/details/16359461 图像遍历主要有三种方法,本节主要介绍和比较这三种方法。 一:简单存取像素值        首先介绍一个名词—椒盐噪点:它是一种特殊的噪点,它随机的将图像中的部分像素设置为白色或者黑色。 Code: [cpp] view...

二十、oracle pl/sql基础

一、pl/sql developer开发工具pl/sql developer是用于开发pl/sql块的集成开发环境(ide),它是一个独立的产品,而不是oracle的一个附带品。 二、pl/sql介绍开发人员使用pl/sql编写应用模块时,不仅需要掌握sql语句的编写方法,还 要掌握pl/sql语句及语法规则。pl/sql编程可以使用变量和逻辑控制语句,从...

[转]awk命令简介

    在shell命令或编程中,可以用AWK强大的的文本处理能力。如果要格式化报文或从一个大的文本文件中抽取数据包,那么awk可完成这些任务。awk是一种解释的编程语言。awk也是shell过滤工具中最难掌握的。awk是一种自解释的编程语言。结合awk和sed和grep,将会使awk编程更加容易。 awk语言最基本的功能是在文件或字符串中基于指定的规则浏...

PL/pgSQL学习笔记之四

http://www.postgresql.org/docs/9.1/static/plpgsql-structure.html 39.2. PL/pgSQL 的结构 PL/pgSQL是一种块式结构的语言。完整的函数定义必须是一个块。一个块的定义形式如下: [<<label>> ] [DECLARE declaratio...

2.3、Python迭代器、列表解析及生成器(0530)

1、动态语言 sys.getrefcount()      //查看对象的引用计数 增加对象的引用计数场景 对象创建时:以赋值的方式,创建变量名的同时就会创建变量 将对象添加进容器时:类似list.append() 当对象被当作参数传递给函数时 多重目标赋值时:s1 = s2 = s3 = 'abc' 为对象创建另外的变量名 减少引用计数 引用此对象...