• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 浅谈c++ stl迭代器失效的问题

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

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c++ stl,c++ stl容器,c++ stl标准程序库,c++ stl教程,c++ stl sort等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

之前看《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);
    }
  }  
</div>

没错,程序崩溃!删除了迭代器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;
  }
</div>

 正如我所想,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;  
  }
</div>

运行了一下,这样是没错的。我打印了调试信息,发现与之前一样,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;
    }
  }
</div>

你猜怎么着??

啥事儿没有!你可能会问,插入之后为什么要++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;
    }
  }
</div>

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;
    }
  }
</div>

我插入之后,直接让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);
  }
</div>

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

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

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

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

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

</div>
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++中 map的基本操作
  • C++中 STL list详解及简单实例
  • 浅谈c++ stl迭代器失效的问题
  • C++ STL list 遍历删除出错解决方案
  • c++中容器之总结篇
  • C++在成员函数中使用STL的find_if函数实例
  • 浅析C/C++中sort函数的用法
  • 深入解析C++ STL中的常用容器
  • C++ 关于STL中sort()对struct排序的方法
  • C++利用容器查找重复列功能实现

相关文章

  • 2017-05-28详解C++编程中的析构函数
  • 2017-05-28C++与C#互调dll的实现步骤
  • 2017-05-28C/C++的浮点数在内存中的存储方式分析及实例
  • 2017-05-28c++获取sqlite3数据库表中所有字段的方法小结
  • 2017-05-28求素数,用vector存储的实现方法
  • 2017-05-2816种C语言编译警告(Warning)类型的解决方法
  • 2017-05-28标准C++类string的Copy-On-Write技术
  • 2017-05-28C++程序设计-五子棋
  • 2017-05-28C++中 静态局部变量实例详解
  • 2017-05-28全排列算法的非递归实现与递归实现的方法(C++)

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C语言编写基于TCP和UDP协议的Socket通信程序示例
    • C语言 数据结构中栈的实现代码
    • 显示任何进程加载的DLL文件的代码
    • C++实现十六进制字符串转换为十进制整数的方法
    • 平衡二叉树AVL操作模板
    • C++函数中return语句的使用方法
    • C++设计模式之策略模式
    • 常用的STL查找算法
    • 详解C语言中index()函数和rindex()函数的用法
    • 深入解析最长公共子串

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有