花整整一天看完了C++primer的第9章,顺序容器。效率之低完全是这该死的迭代器拖的后腿。回来百度到别人的博客才有了一丢丢理解~以下开始厚颜无耻的转文章了。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

【转】

众所周知当使用一个容器的insert或者erase函数通过迭代器插入或删除元素"可能"会导致迭代器失效,因此很多建议都是让我们获取insert或者erase返回的迭代器,以便用重新获取新的有效的迭代器进行正确的操作:

view plaincopy to clipboardprint?
iter=vec.insert(iter);   
iter=vec.erase(iter);      
  想想究竟为什么迭代器失效,原因也不难理解。以vector为例,当我们插入一个元素时它的预分配空间不够时,它会重新申请一段新空间,将原空间上的元素复制到新的空间上去,然后再把新加入的元素放到新空间的尾部,以满足vector元素要求连续存储的目的。而后原空间会被系统撤销或征做他用,于是指向原空间的迭代器就成了类似于“悬垂指针”一样的东西,指向了一片非法区域。如果使用了这样的迭代器会导致严重的运行时错误就变得很自然了。这也是许多书上叙述vector在insert操作后“可能导致所有迭代器实效”的原因。但是想到这里我不禁想到vector的erase操作的叙述是“会导致指向删除元素和删除元素之后的迭代器失效”。但是明显感觉erase带来失效要比insert来得轻得多。似乎“此失效非彼失效”,想想似乎也是这样的:erase操作是在原空间上进行的,假设有一个存有"12345"序列的vector<int>容器原本指向3的迭代器在我删除2之后无非变成指向4了,我只要注意别用到超过end位置的迭代器不就行了吗?

说了这么多似乎可以归纳一下迭代器失效的类型了:

1.由于容器元素整体“迁移”导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。

2.由于删除元素使得某些元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。

对于第一种类型没什么好就是的了,原因应该确定如此了。可对于第二种,我写了如下的代码

view plaincopy to clipboardprint?
vector<int> vec;   
for(int i=0;i<10;i++)   
vec.push_back(i);   
 
vector<int>::iterator iter =vec.begin()+2;   
vec.erase(iter);//注:这里真的不建议这么写   
cout<<*iter<<endl;   
for(vector<int>::iterator it=vec.begin();it!=vec.end();it++)   
cout<<*it<<endl;

按照我的猜测尽管我在注释的位置的写法很“危险”,但是我并未涉及到上面总结第一种失效类型的范畴。程序应该还是会如预期的一样删除在vec[2]位置上的2然后输出前移到vec[2]位置上的3,并输出0到10不含2的所有数字,于是vs2008的c++环境下运行一下,竟然一个是个无情的“红叉”。按道理说这种写法是绝对不对出现严重的运行时错误的,难道是猜测得不对吗?

当时被这样的疑问困扰了几天,有一天突然想起一个关于容器迭代器作参数的例子,在vs2008下运行不了,但是在vc6.0下却可以,有人说是因为08采用了更为严格的类型检测机制。于是将上面的代码放到6.0里,果然得到了预期的效果,看来系统果然是这么处理的。至于08为什么不可以,现在我只能认为是采用了更加安全的检查机制。使得第二种类型的失效后果同样“不可饶恕”。

这样我就又想到假如insert元素时原空间够用的话,是不是也不会产生第一类失效而产生第二类失效呢?

view plaincopy to clipboardprint?
vector<int> vec;   
for(int i=0;i<10;i++)   
    vec.push_back(i);   
       cout<<"capacity:"<<vec.capacity()<<endl;//查看预分配空间大小   
vector<int>::iterator iter =vec.begin()+2;   
vec.insert(iter,100);   
cout<<"capacity:"<<vec.capacity()<<endl;     
cout<<*iter<<endl;   
for(vector<int>::iterator it=vec.begin();it!=vec.end();it++)   
cout<<*it<<endl;  
   
  同样在vc6.0下,证明了假设是对的。但是上面的种种做法只是为了帮助我理解迭代器实效的原因,建议使用insert和erase操作时还是像许多书中介绍的如第一段代码那样的写法,这是一种好的且安全的习惯。总之一句话去相信“insert和erase操作后所有的迭代器都会失效”。

迭代器(iterator)是一个可以对其执行类似指针的操作(如:解除引用(operator*())和递增(operator++()))的对象,我们可以将它理解成为一个指针。但它又不是我们所谓普通的指针,我们可以称之为广义指针,你可以通过sizeof(vector::iterator)来查看,所占内存并不是4个字节。

首先对于vector而言,添加和删除操作可能使容器的部分或者全部迭代器失效。那为什么迭代器会失效呢?vector元素在内存中是顺序存储,试想:如果当前容器中已经存在了10个元素,现在又要添加一个元素到容器中,但是内存中紧跟在这10个元素后面没有一个空闲空间,而vector的元素必须顺序存储一边索引访问,所以我们不能在内存中随便找个地方存储这个元素。于是vector必须重新分配存储空间,用来存放原来的元素以及新添加的元素:存放在旧存储空间的元素被复制到新的存储空间里,接着插入新的元素,最后撤销旧的存储空间。这种情况发生,一定会导致vector容器的所有迭代器都失效。

我们看到实现上述所说的分配和撤销内存空间的方式以实现vector的自增长性,效率是极其低下的。为了使vector容器实现快速的内存分配,实际分配的容器会比当前所需的空间多一些,vector容器预留了这些额外的存储区,用来存放新添加的元素,而不需要每次都重新分配新的存储空间。你可以从vector里实现capacity和reserve成员可以看出这种机制。

capacity和size的区别:size是容器当前拥有的元素个数,而capacity则指容器在必须分配新存储空间之前可以存储的元素总数。

vector迭代器的几种失效的情况: 1.当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。 2.当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效。 3.当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。

deque迭代器的失效情况: 在C++Primer一书中是这样限定的: 1.在deque容器首部或者尾部插入元素不会使得任何迭代器失效。 2.在其首部或尾部删除元素则只会使指向被删除元素的迭代器失效。 3.在deque容器的任何其他位置的插入和删除操作将使指向该容器元素的所有迭代器失效。但是:我在vs2005测试发现第一条都不满足,不知为何?等以后深入STL以后慢慢的领会吧!

只有list的迭代器好像很少情况下会失效。也许就只是在删除的时候,指向被删除节点的迭代器会失效吧,其他的还没有发现。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以下纯属个人猜想,有待考证~

看了上述文章,我个人觉得迭代器应该是按存储顺序有内在编号的,当利用迭代器iterator(假如内置编号为3)插入一个元素或删除一个元素后,迭代器iterator原来的内置编号已经和现在元素的存储顺序不匹配,于是需要语句

iter=vec.insert(iter);   
iter=vec.erase(iter);

进行重置指向,因为iterator内置编号还是3,所以要将iterator重新指向第三个元素;

转载于:https://www.cnblogs.com/fsliu/p/4134071.html

顺序容器迭代器之鄙见相关推荐

  1. 容器大小_C++ 顺序容器基础知识总结

    阅读目录 0.前言 1.容器概论 2.std::array 2.1.底层数据结构 2.2.内存分配策略 2.3.array的优势在哪 3.forward_list 3.1.底层数据结构 3.2.for ...

  2. c++ array容器 传参_C++ 顺序容器基础知识总结

    阅读目录 0.前言 1.容器概论 2.std::array 2.1.底层数据结构 2.2.内存分配策略 2.3.array的优势在哪 3.forward_list 3.1.底层数据结构 3.2.for ...

  3. C++知识点17——使用C++标准库(顺序容器vector常用操作)

    C++STL中的容器类型分为两种,一种是顺序容器,另一种是关联容器,这两种容器之所以被划分,本质区别是顺序容器可以通过元素在容器中的位置进行访问及存储,而关联容器只能通过键来访问和存储元素 顺序容器常 ...

  4. 顺序容器及其常用函数

    前言 容器是指容纳特定类型对象的集合.顺序容器则是指该容器根据位置访问保存在其中的对象.Vector,List,Deque是三种常见的容器,本文将归纳这三种容器常用的函数,以备日后查阅. 描述符说明 ...

  5. 《C++ Primer 5th》笔记(9 / 19):顺序容器

    文章目录 顺序容器概述 确定使用哪种顺序容器 容器库概览 迭代器 迭代器范围 使用左闭合范围蕴含的编程假定 容器类型成员 begin和end成员 容器定义和初始化 将一个容器初始化为另一个容器的拷贝 ...

  6. C++primer第九章 顺序容器 9.5 额外的string操作

    除了顺序容器共同的操作之外,string类型还提供了一些额外的操作.这些操作中 的大部分要么是提供string类和C 风格字符数组之间的相互转换,要么是增加了允许我们用下标代替迭代器的版本. 标准库s ...

  7. 顺序容器(1):vector

    1.容器与顺序容器 容器:特定类型对象的集合. 顺序容器:提供 控制 对集合里的 元素 存储和访问 顺序 (这种顺序与元素加入容器时的位置相对应)的能力. 有:vector,deque,list,fr ...

  8. C++primer十万字笔记 第九章 顺序容器

    顺序容器   容器就是特定类型对象的集合,顺序容器为程序员提供了控制元素存储和访问顺序的能力.这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应.标准库提供了三种容器类型. 顺序容器概述  标 ...

  9. 2 C++标准库(1- IO库、顺序容器和泛型算法)

    2-1 C++标准:IO库.顺序容器和泛型算法 8 IO库 8.1 IO类 8.2 文件输入输出 8.2.1 使用文件流对象 8.2.2 文件模式 8.3 string流 8.3.1 使用istrin ...

最新文章

  1. 在温系统下制作马克系统引导安装镜像启动U盘安装马克OS
  2. 01python语言程序设计基础——初识python
  3. gateway java_基于SpringCloudGateway 实现的网关
  4. java web项目的目录结构以及各文件夹的功能是什么eclipse的web目录及各作用
  5. glutSolidSphere 用例
  6. Linux C++多线程同步的四种方式
  7. cuda 图片拆分_急需,PDF怎么拆分啊?
  8. 12.整数转罗马数字-LeetCode
  9. 触目惊心,北邮计算机学院女神居然每天看这些公众号
  10. LCD 驱动的整体分析。
  11. 用Proxy进行预处理
  12. Linux之shell命令
  13. koa2 mysql增删改查_使用nodejs-koa2-mysql-sequelize-jwt实现登录注册,文章增删改查接口...
  14. DOS原理和常用命令详解示例
  15. 用户画像、用户分群、用户分层,到底有啥区别?
  16. Oracle 10g client(instantclient)环境配置
  17. 2021年全国计算机能力挑战赛C++决赛,题目分享
  18. Linux文件目录操作命令 rm
  19. 蓝蓝算法第二期,T51
  20. RF自动化测试框架(一)

热门文章

  1. 清理C盘的几种方法,帮你解决C盘爆红的烦恼
  2. 【AMD、CMD和CommonJS】
  3. 宝塔面板建立的网站为什么访问不了
  4. Navicat Premium 简介、安装、使用
  5. 一次精彩的皮卡车降噪试验过程
  6. 极速office(Word)如何在表格里面插入行或者列
  7. 360手机刷机·LSPosed安装和使用教程
  8. [二分][dp凸优化] Luogu P4383 林克卡特树lct
  9. 信息隐藏——扩频水印
  10. 微信errcode大全