浅谈c++ stl迭代器失效的问题

之前看《C++ Primier》的时候,也解到在顺序型窗口里insert/erase会涉及到迭代器失效的问题,并没有深究。今天写程序的时候遇到了这个问题。

1 莫名其妙的Erase

最初我的程序是酱紫的,别说话,我知道这样是有问题的,可这样是最直观的想法

int arr[]={0,1,2,3,4,5,6,7,8,9,10};
  vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));for (auto it = a.begin(); it != a.end();++it ){
    if ((*it)&1){
      a.erase(it);
    }
  }  

没错,程序崩溃!删除了迭代器it之后,it迭代器失效了,无法再进行++it操作了。

可是,当我觉得erase做的只是把it之后的元素向前移动一个位置而已,为什么迭代器失效了呢?我翻开《STL源码剖析》,SGI STL的vector<T,Alloc>::erase的源码是这样的:

iterator vector<T, Alloc>::erase(iterator position)
  {
    if (position + 1 != end())
      copy(position + 1, finish, position);
    --finish;
    destroy(finish);
    return position;
  }

正如我所想,erase函数并没有对输入的position迭代器进行改写!我打印出调试信息,发现erase之后,迭代器的_Ptr成员,也就是指针的值并没有发生变化,而此指针所指的元素的确是下一个元素。那么为什么失效了呢?

我又查了《C++ Primier》,发现此书上的标准写法是这样的:

int arr[]={0,1,2,3,4,5,6,7,8,9,10};
  vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));
  for (auto it = a.begin(); it != a.end();){
    if ((*it)&1){
      it=a.erase(it);
    }
    else
      ++it;
  }

运行了一下,这样是没错的。我打印了调试信息,发现与之前一样,erase之后把结果赋给it,it里的成员_Ptr并没有发生变化。唯一的可能就是迭代器里还有别的标志,如果当前元素被删除之后,该迭代器也就“失效”了。《C++ Primier》并未对此作出过多解释,只是说,erase函数返回被删除元素的下一个元素的迭代器。

结论:在STL里,我们不能以指针来看待迭代器,指针是与内存绑定的,而迭代器是与容器里的元素绑定的,删除了之后,该迭代器就失效了,在对其重新赋值之前,不能再访问此迭代器。

2 更加小心冀冀地Insert

机智如我,自然会去探索一下insert之后,迭代器会怎样。于是: 

vector<int> a;
  for (int i = 0; i < 10; ++i)
  {
    a.push_back(i);
  }

  for (auto it = a.begin(); it != a.end(); ++it){
    if (*it == 5){
      a.insert(it, 100);
       ++it;
    }
  }

你猜怎么着??

啥事儿没有!你可能会问,插入之后为什么要++it。插入之前,it指向5,在5之前插入100后,it指向100。这样下一次循环,it依然会指向5。相信我,你的程序会爆炸的!

我作了个++it之后,it又指向5,下一次循环就直接指向5之后的元素了,顺利完成插入工作。

世界和平~世界和平~我还真不确定。

突然想到,当插入元素过多,vector的capacity会增加,这时会不会问题呢?说干就干:

vector<int> a;
  for (int i = 0; i < 13; ++i)
  {
    a.push_back(i);
  }

  for (auto it = a.begin(); it != a.end(); ++it){
    if (*it == 5){
      a.insert(it, 100);
       ++it;
    }
  }

BOOM!果然崩溃了!也就是说插入之后的迭代器失效了。那之前的呢?

我决定粗暴地测试一下:

vector<int> a;
  for (int i = 0; i < 13; ++i)
  {
    a.push_back(i);
  }
  auto it1=a.begin();
  for (auto it = it1; it != a.end(); ++it){
    if (*it == 5){
      a.insert(it, 100);
       it=it1;
    }
  }

我插入之后,直接让it指向begin(),然后单步调试。执行完it=it1还好好的,可再去执行++it还是崩溃了。

也就是说,capacity变化之后,所有的迭代器都失效了!这是当然了呀!capacity发生变化,容器内部做的不仅仅是增加capacity这么简单,因为容器所在内存后面可能没有足够的内存让我们使用,所以,容器要重新开辟一段足够大的内存来存储容器里的元素,当前内存会被释放。这样一来,迭代器自然失效了。

3 C++ Primier的总结

关于容器的迭代器失效的问题,C++ Primier用了一小节作了总结,我翻译成中文如下:

(1)增加元素到容器后

对于vector和string,如果容器内存被重新分配,iterators,pointers,references失效;如果没有重新分配,那么插入点之前的iterator有效,插入点之后的iterator失效;

对于deque,如果插入点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,deque的迭代器失效,但reference和pointers有效;

对于list和forward_list,所有的iterator,pointer和refercnce有效。

(2)从容器中移除元素后

对于vector和string,插入点之前的iterators,pointers,references有效;off-the-end迭代器总是失效的;

对于deque,如果插入点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,off-the-end失效,其他的iterators,pointers,references有效;

对于list和forward_list,所有的iterator,pointer和refercnce有效。

(3)在循环中refresh迭代器

当处理vector,string,deque时,当在一个循环中可能增加或移除元素时,要考虑到迭代器可能会失效的问题。我们一定要refresh迭代器。

int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  deque<int> v(arr,arr+sizeof(arr)/sizeof(*arr));
  for (auto it = v.begin(); it != v.end(); )
  {
    if ((*it) & 1)
    {
      it = v.insert(it, *it);
      it += 2;
    }
    else
      it = v.erase(it);
  }

至于it+=2,很容易解释,insert之后,it指向新增加的元素,+2之后,it指向下一个要处理的元素。

(4)在循环不变式中不要store off-the-end迭代器

这个很容易理解了,增加或移除元素之后,off-the-end失效了,不store的话,每次从end()函数中取的都是最新的off-the-end,自然不会失效。

最后:《C++ Primier》是本好书。

以上就是小编为大家带来的浅谈c++ stl迭代器失效的问题全部内容了,希望大家多多支持我们~

(0)

相关推荐

  • C++ STL入门教程(3) deque双向队列使用方法

    一.简介 deque(Double Ended Queues,双向队列)和向量很相似,但是它允许在容器头部快速插入和删除(就像在尾部一样). 二.完整程序代码 /*请务必运行以下程序后对照阅读*/ #include <deque> #include <iostream> #include <algorithm> #include <stdexcept> using namespace std; void print(int num) { cout <&

  • C++ STL入门教程(1) vector向量容器使用方法

    一.简介 Vectors 包含着一系列连续存储的元素,其行为和数组类似. 访问Vector中的任意元素或从末尾添加元素都可以在O(1)内完成,而查找特定值的元素所处的位置或是在Vector中插入元素则是O(N). 二.完整程序代码 /*请务必运行以下程序后对照阅读*/ #include <vector> #include <iostream> #include <algorithm> #include <stdexcept> using namespace

  • C++ STL list 遍历删除出错解决方案

    C++ STL list 遍历删除崩溃 错误用法一 下面这种用法会在for的地方崩溃,分析 第一次for循环的时候 it=0,当t.erase(it)执行完成之后 it就变成了 -17891602 表明it不能再作为迭代器进行运算,自然会报错. #include <map> #include <list> using namespace std; typedef std::list<int > TESTLIST; int _tmain(int argc, _TCHAR*

  • 深入解析C++ STL中的常用容器

    STL是C/C++开发中一个非常重要的模板,而其中定义的各种容器也是非常方便我们大家使用.下面,我们就浅谈某些常用的容器.这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中的常用容器包括:顺序性容器(vector.deque.list).关联容器(map.set).容器适配器(queue.stac). 1.顺序性容器 (1)vectorvector是一种动态数组,在内存中具有连续的存储空间,支持快速随机访问.由于具有连续的存储空间,所以在插入和删除操作方面,效率比较慢

  • C++在成员函数中使用STL的find_if函数实例

    本文实例讲述了C++在成员函数中使用STL的find_if函数的方法.分享给大家供大家参考.具体方法分析如下: 一般来说,STL的find_if函数功能很强大,可以使用输入的函数替代等于操作符执行查找功能(这个网上有很多资料,我这里就不多说了). 比如查找一个数组中的奇数,可以用如下代码完成(具体参考这里:http://www.cplusplus.com/reference/algorithm/find_if/): #include <iostream> #include <algori

  • c++ STL容器总结之:vertor与list的应用

    STL提供六大组件,彼此可以组合套用 1.容器(containers):各种数据结构,如vertor,list,deque,set,map.从实现的角度来看,STL容器是一种class template 2.算法(algorithms):各种算法如sort,search,copy,earse.STL算法是一种 function template. 3.迭代器(iterators):扮演容器与算法之间的胶合剂,是所谓的"泛型指针".所有STL容器都有自己的专属的迭代器. 4.仿函数(fu

  • 浅谈c++中的stl中的map用法详解

    Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道.这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处. 下面举例说明什么是一对一的数据映射.比如一个班级中,每个学生的学号跟他的姓名就存在着一一

  • C++ STL入门教程(2) list双向链表使用方法(附程序代码)

    一.简介 "Unlike other standard sequence containers, list and forward_list objects are specifically designed to be efficient inserting and removing elements in any position, even in the middle of the sequence." Lists将元素按顺序储存在链表中.与向量(vector)相比, 它允许快速

  • c++非变易算法-stl算法

    C++ STL标准模板库在数据结构和算法的实践领域发挥着重要作用,极大的提高了开发效率.STL的三大组成部分为容器.迭代器.算法,本文主要讲解STL算法中的非变易算法.本文从实践的角度简单介绍了一下相关函数的使用. C++ STL的非变易算法(Non-mutating algorithms)是一组不破坏函数数据的模板函数,用来对序列数据进行逐个处理.元素查找.子序列搜索.统计和匹配,基本上可用于各种容器.下面的叙述中迭代器区间默认为[first, last),迭代器具有"++"迭代和&

  • C++ 关于STL中sort()对struct排序的方法

    前言 一直没有系统去看过c++,因为懂得一些c的基本语法,在实际编程中用到c++,只能用到哪些看哪些,发现这样虽然能够完成大部分工作,但是有时候效率实在太低,比如说这节要讲的Std::sort()函数的使用,调了半天才调通.开通c/c++序列博客是记录在使用c++中一些难题,避免以后重犯错,当然以后会尽量挤出时间来较系统学习下c++. 开发环境:QtCreator2.5.1+OpenCV2.4.3 实验基础 首先来看看std中的快速排序算法sort的使用方法: template <class R

随机推荐