C++基本语言:1.9迭代器精彩演绎,失效分析及弥补、实战
C++基本语言包含10章节内容,存于C++从入门到精通专栏
目录
三、迭代器begin()/end()、反向迭代器rbegin/rend操作
五、迭代器运算符 const_iterator迭代器(只读)
一、迭代器简介
vector是一个容器,里面可以容纳很多对象。
迭代器是一种遍历容器内元素的 数据类型。
这种数据类型感觉有点像指针,读者就理解为迭代器是用来指向容器中的 某个元素 的。
可以通过“[]”(下标)访问string字符串中的字符、访问vector中的元素。
但实际上,在C++中,很少通过下标来访问它们,一般都是采用迭代器来访问(更通用)
因为除了vector容器外,C++标准库中还有几个其他种类的容器。这些容器都可以使用迭代器来遍历其中的元素内容。
string其实是字符串,不属于容器,但string也支持用迭代器遍历。
通过迭代器,可以读取容器中的元素值、修改容器中某个迭代器所指向的元素值。
此外,迭代器可以像指针一样,通过++、--等 运算符从容器中的一个元素移动到另一个元素。
在C++标准库中,还有其他容器如list、 map等都属于比较常用的容器,C++标准库为每个这些容器都定义了对应的一种迭代器类型,有很多容器不支持“[]”操作,但容器都支持迭代器操作。
结论:尽量用迭代器来访问容器中的元素。 ?
二、容器的迭代器类型
C++标准库为每种容器都定义了对应的迭代器类型。这里就以容器vector为例
这个变量的类型是vector<int>::iterator类型
“::iterator”是每个容器(如vector)里面都定义了的一个成员(类型名),这个名字是固定的,请牢记。
在理解的时候,就把整个vector<int>::iterator理解成一种类型, 这种类型就专门应用于迭代器,当用这个类型定义一个变量的时候iter,这个变量就是一个迭代器。
三、迭代器begin()/end()、反向迭代器rbegin/rend操作
1.迭代器
每一种容器,如vector,都定义了begin和end的成员函数
1.1begin和end
这两个成员函数正好用来返回迭代器类型。
(1)begin返回一个迭代器类型(就理解成返回一个迭代器)
(2)end返回一个迭代器类型(就理解成返回一个迭代器)
对上面的代码进行跟踪调试,观察begin和end结果可以看到, end()指向了一个乱数字
(3)如果容器为空,则begin返回的迭代器和end返回的迭代器相同。
结论:end返回的迭代器并不指向容器vector中的任何元素,它起到实际上是一个标志(岗哨)作用,如果迭代器从容器的begin位置开始不断往后游走,也就是不断遍历容器中的元素,那么如果有一个时刻,iter 走到了end位置,那就表示已经遍历完了容器中的所有元素。
(4)写一段代码,传统的通过迭代器访问容器中元素的方法(100,200,300)
1.2?反向迭代器 rbegin()和rend()
想从后面往前遍历一个容器,那么反向迭代器就比较方便。
反向迭代器使用的是rbegin成员函数和rend成员函数。
?1)rbegin返回一个反向迭代器类型,指向容器的最后一个元素。
?2)rend返回一个反向迭代器类型,指向容器的第一个元素的前面位置。
范例:运行起来看结果:300、200、100
四、迭代器运算符
(1)*iter:返回迭代器iter所指向元素的引用。(类似于*p)
必须要保证该迭代器指向的是有效的容器元素,不能指向end,因为end是末端元素后面的位置 ,也就是说,end 已 经指向了一个不存在元素 。
(2)++iter/iter++:让迭代器指向容器中的下一个元素。
但是已经指向end的迭代器,不能再++,否则运行时报错。
(3)--iter 和 iter--: 让迭代器指向容器中的前一个元素。
但是已经指向begin的迭代器,不能再--,否则运行时报错。
(4)iter1==iter2或iter1!=iter2:判断两个迭代器是否相等。如果两个迭代器指向的是同一个元素,就相等,否则就不相等。
(5)如何引用结构中的成员
请注意:一定要确保迭代器指向有效的容器中的元素,否则范例中的 这些行为可能会导致意想不到的结果。
还有很多其他运算符,例如迭代器之间可以相减表示两个迭代器之间 的距离,迭代器加一个数字表示跳过多少个元素,不过这些都不常用,不准备逐一介绍,意义也不大。
五、迭代器运算符 const_iterator迭代器(只读)
实际上每种容器还有另外一种迭代器类型,叫作const_iterator
含义:有const 在,一般都表示常量,也就是说值不能改变的意思。
这里的值不能改变表示该迭代器指向的元素的值不能改变,并不表示该迭代器本身不能改变。也就是说,该迭代器是可以不断地指向容器中的下一个元素的。
terator只能从容器中读元素,不能通过该迭代器修改容器中的元素。
所以说,const_iterator更像一个常量指针,而 iterator迭代器是能读能写的。
什么时候用const_iterator呢?
如果这个容器对象是一个常量,那么就必须使用const_iterator,否则报错:
cbegin和cend
C++11引入的两个 新函数cbegin和cend成员函数,与begin、end非常类似。
不管容器是否是常量容器,cbegin、cend返回的都是常量迭代器const_iterator。
六、迭代器失效
6.1添加元素和从容器中删除元素操作要小心
如果在for循环中,通过push_back等手段往容器中增加元素,范围 for循环输出的容器中元素就会混乱,要么有结果但混乱,要么直接崩溃。
其实,范围for语句等价于常规的用迭代器对容器进行操作。
等价于迭代器这种操作方式:
一旦在for循环中增删容器中的元素,就会导致迭代器失效, 整个结果就混乱了。
???? 其实,任何一种能够改变vector对象容量的操作,如push_back, 都会使当前的vector对象迭代器失效,请读者谨记:在操作迭代器的过程中(使用了迭代器的这种循环体)
千万不要改变vector对象的容量,也就是不要增加或者删除vector容器中的元素。
对于向容器中添加元素和从容器中删除元素操作要小心,因为这些操作可能都会使指向容器元素的迭代器(也包括指针、引用等)失效。
失效就表示它不能再代表任何容器中的元素,一旦使用这种失效的迭代器,就表示程序的书写犯了严重错误,很多情况下都会导致程序崩溃,就好比使用了没有被初始化的指针一样。
6.2不同的容器实现机理不同
(例如有的容器内部数据是连续存储的,插入元素时一旦原有内存不够用,则可能就会导致容器中原有数据全部迁移到一个新内存去,如vector等容器),不同的插入操作、不同的插入位 置,会导致迭代器、指针、引用部分或者全部失效。
甚至在循环体中的诸如vecvalue.end()代码都会因为插入数据操作导致失效。
另一种情况是删除操作。如果从容器中删除一个元素,那么,当前指向这个被删除元素的迭代器、指针、引用肯定是立即失效,绝不能再引用它们。 此外,不同的容器,针对删除操作,不同的删除位置,也会导致迭代器 、 指 针 、 引用部分或者全部失效 , 甚 至 在 循 环 体 中 的 诸 如 vecvalue.end()代码都会因为删除数据操作导致失效。
解决方法就是:
如果在一个使用了迭代器的循环中插入元素到容器, 那只插入一个元素后就应该立刻跳出循环体,不能再继续用这些迭代器操作容器。
效果:
6.3灾难程序演示
灾难程序演示1
下面代码目前一切没有问题:
接着,往循环中增加代码,注意while循环体中代码的变化:
如果有如下需求:
想不断地插入多条数据,并且还希望迭代器不失效,那就得查资料研究,如研究针对vector容器,如何写 insert这段代码,才能让迭代器不失效,让程序安全地运行。看如下代 码,是一种满足连续插入多条数据的解决方案:
相当于每次更新end,防止end失效
insert后返回的还是迭代器,指向(icount+80)
*beg的值依次是:80,81……90
太细节的东西就不过多涉及,迭代器会失效的道理读者都懂了。
一般的做法就是如果这个循环和迭代器有关,基本都只会做插入或删除操作一次,然后会立即break,因为保不准插入或删除操作导致哪个迭代器失效,所以立即break到循环体到外面去。
灾难程序演示2
在一个程序运行结束之前,可能会习惯性地释放掉vector里面的内容。
有些人可能会写出这样的代码来在程序的最后进行释放处理:
运行后程序崩溃。肯定是迭代器失效导致崩溃。那就一定得要小心这种释放代码。
经过分析,erase会返回下一个元素位置,这个位置肯定要想办法保存,但因为这里用的是for循环,for循环里每次还有++iter这种操作,所 以怎样改造能够让它安全释放,这是一个问题。经过思考和研究,找到了 一种写法能够让容器顺利释放:
所以,如果说要把容器一下全部清空,用clear还是其他方法也好, 都还算简单。
可以使用 iv.clear();
但是如果需要用和迭代相关的循环来一个元素一个元素地删除,那一定更要注意。
这里笔者推荐一个简单直接且有效的方法:
七、范例演示
1)用迭代器遍历string类型数据
2)vector容器常用操作与内存释放
实践程序:
假设有一些配置项,配置项里记录一些配置数据,当然这些配置项正常来讲应该写在文件中,这样方便随时修改,但因为是演示目的,就写在代码中即可。
然后是主函数
这里用到了一个函数
看一看conflist容器结构示意图
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!