一、迭代器简介

迭代器是一种遍历容器内元素的数据类型。这种数据类型感觉有点像指针,读者就理解为迭代器是用来指向容器中的某个元素的。

string可以通过[ ](下标)访问string字符串中的字符,vector可以通过[ ]访问vector中的元素。在C++中,很少通过下标访问它们,一般都是才有迭代器来访问。

除了vector容器外,C++标准库中还有几个其它种类的容器。这些容器都可以使用迭代器来遍历其它的元素内容。string其实是字符串,不属于容器,但string也支持用迭代器遍历。

通过迭代器,可以读取容器中的元素值、修改容器中某个迭代器所代表(所指向)的元素值。此外,迭代器可以像指针一样通过++(自加)、--(自减)等运算符从容器中的一个元素移动到另外一个元素。

C++标准库中有很多容器,并为每个容器定义了对应的一种迭代器类型,有很多容器不支持[]下标操作,但是容器都支持迭代器操作。建议在访问容器中的元素时不用下标访问,而是用迭代器来访问容器中的元素。

二、容器的迭代器类型

上面讲过,C++标准为每种容器都定义了对应的迭代器类型。这里就以容器vector为例。

vector <int> myInt1={100,200,300};  //定义一个容器

vector <int> ::iterator iter;       //定义迭代器,因为容器是以vector <int> 开头,所以迭代器也必须是以vector <int> 开头

可以把vector <int> ::iterator看成iterator迭代器类型,当用这个类型定义一个变量时,这个变量就是一个迭代器变量。

三、迭代器begin/end、反向迭代器rbegin/rend操作

1、迭代器

每一种容器,如vector,都定义了一个叫begin的成员函数和一个叫end的成员函数。这两个成员函数正好用来返回迭代器类型。

(1)begin返回一个迭代器类型.

int *p = NULL;
int  arrayData[] = {400,500,600};
p = arrayData;      //指针p指向arrayData首地址
cout << *p << endl; //打印p指向arrayData首地址存储的元素vector <int> myInt1 = { 100,200,300 };  //定义一个容器
vector <int> ::iterator iter;       //定义迭代器,因为容器是以vector <int> 开头,所以迭代器也必须是以vector <int> 开头iter = myInt1.begin();//如果myInt1不为空,则返回myInt1第一个元素,即myInt1[0]里面的内容。类似于iter指针指向myInt1.begin首地址。
cout << *iter << endl;

(2)end返回一个迭代器类型(理解成返回一个迭代器)。

iter = myInt1.end();//end返回的迭代器指向的并不是末端元素,而是末端元素的后面,end返回的内容类似于C语言字符串数组中的'\0'结束符。

(3)如果容器为空,则begin返回的迭代器和end返回的迭代器指向位置相同。

vector <int> myInt2;
vector <int> ::iterator iterBegin;
vector <int> ::iterator iterEnd;
iterBegin = myInt2 .begin();
iterEnd   = myInt2 .end();if(iterBegin == iterEnd)
{cout << "容器为空" << endl;
}

下面用示意图对begin和end在迭代器中的指向进行说明:

end返回的迭代器并不指向容器vector中的任何元素,它仅是起到一个容器内容结束标志作用。如果迭代器从容器的begin位置不断往后偏移,当偏移到end位置,表示已经遍历了容器中的所有元素。

(4)通过迭代器访问容器中的元素

vector <int> myInt1 = { 100,200,300 };  //定义一个容器
vector <int> ::iterator iter;
for(iter = myInt1.begin();iter != myInt1.end();iter++)
{cout << *iter << " ";
}
cout << endl;

 2、反向迭代器

如果想从后面往前面遍历一个容器,用反向迭代器比较方便。反向迭代器使用的成员函数为rbegin和rend。

(1)rbegin返回一个反向迭代器类型,指向容器的最后一个元素。

(2)rend返回一个反向迭代器类型,指向容器的第一个元素的前面位置。

vector <int> myInt1 = { 100,200,300 };  //定义一个容器vector <int> ::reverse_iterator iter;
for (iter = myInt1.rbegin(); iter != myInt1.rend(); iter++)
{cout << *iter << " ";
}cout << endl;

 四、迭代器运算符

1、*iter:返回迭代器iter所指向的引用。必须保证iter迭代器指向有效容器的元素,不能指向无效的end或者rend。

2、++iter(iter++):迭代器自加。让迭代器指向容器中的下一个元素,但已经指向end后就不能再++,否则系统报错。

3、--iter(iter--):迭代器自减。让迭代器指向容器中的前一个元素,但已经指向rend后就不能再--,否则系统报错。

4、iter1 == iter2或iter1 != iter2:判断两个迭代器是否相等。两个迭代器指向同一个元素则相等,否则不等。

5、结构体成员引用。

vector <student> vStu;
student mystu;mystu.num = 100;
vStu.push_back(mystu);//把对象mystu复制到vStu容器中,vStu和mystu没有直接关系
mystu.num = 200;//不会改变容器中元素的值,容器中内容是复制进去的vector <student>::iterator iter;
iter = vStu.begin();//指向第一个元素cout << (*iter).num << endl;//100,*iter是一个结构体变量,用"."成员来引用成员。
cout << iter->num << endl;//100,iter想象成一个指针,所以“->”引用成员请注意:一定要确保迭代器指向有效的容器中的元素,否则会导致意想不到的结果。

五、const_iterator常量迭代器

常量迭代器只能从容器中读取元素,不能通过改迭代器修改容器中的元素。const_iterator更像一个常量指针。如下例子可以更改容器里面的元素值:

vector <int> myInt1 = { 100,200,300 };  //定义一个容器
int data[] = {10,20,30};//定义一个data数组
int i = 0;//定义一个int类型变量vector <int> ::iterator iter;for (iter = myInt1.begin(),i = 0;(i<sizeof(data))&&(iter != myInt1.end()); iter++,i++)//遍历myInt1元素和data数组元素
{*iter = *iter+data[i];//修改myInt1容器的元素值
}for (iter = myInt1.begin(); iter != myInt1.end(); iter++)//遍历myInt1元素
{cout << *iter << " ";//遍历myInt1元素
}
cout << endl;

我们把上面“vector <int> ::iterator iter;”中的iterator迭代器类型改成常量迭代器“const_iterator”,再运行程序,系统报错:“iter不能给常量赋值”,如下图所示:

可以看到,常量迭代器不能修改容器里面元素的值,我们把上面修改容器元素值的for循环屏蔽掉,上面程序正常运行,说明常量迭代器可以遍历不带const的容器。需要注意的是,如果容器是常量容器,则必须用常量迭代器来访问和遍历,否则会报错。

const vector <int> myInt1 = { 100,200,300 };  //定义一个常量容器
vector <int> ::const_iterator iter;//必须定义一个常量迭代器for (iter = myInt1.begin(); iter != myInt1.end(); iter++)//遍历myInt1元素
{cout << *iter << " ";//遍历myInt1元素
}
cout << endl;

在C++11中引入两个函数,cbegin和cend,无论容器是否是常量容器,cbegin和cend返回的都是常量迭代器const_iterator。

六、迭代器失效

在操作迭代器的过程中(如使用迭代器的for循环体),千万不要在循环体内改变vector对象容量的操作,比如增加、删除vector容器中的元素。如:

for(auto beg = vecvalue.begin(),end = vecvalue.end();beg != end;++beg)
{//在这个循环体内不要改变vecvalue对象的容量
}

对于向容器中添加元素或者从容器中删除元素操作要小心,因为这些操作可能都会使指向容器元素的迭代器(包括指针、引用)失效。

解决的方法:如果在一个使用了迭代器的循环体中插入元素到容器,只插一个元素后就应该立即跳出循环体,不能再继续使用这些迭代器操作容器。如下所示:

for(auto beg = vecvalue.begin(),end = vecvalue.end();beg != end;++beg)
{vecvalue.push_back(123);//push_back函数可能带来内存溢出break;//立刻跳出循环
}
//重新定位迭代器
for(auto beg = vecvalue.begin(),end = vecvalue.end();beg != end;++beg)
{
......
}

下面讲进行一些灾难程序演示:

(1)灾难程序演示1:

vector <int> myInt1 = { 100,200,300 };  //定义一个容器
auto beg = myInt1.begin();
auto end = myInt1.end();
while(beg != end)
{cout << *beg << endl;
}接着在循环中增加代码,注意while循环体中代码的变化:
vector <int> myInt1 = { 100,200,300 };  //定义一个容器
auto beg = myInt1.begin();
auto end = myInt1.end();
while(beg != end)
{cout << *beg << endl;myInt1.insert(beg,80);//在begin这个位置插入新元素,可以用insert,插入新元素容量不断加大,begin和end位置可能已经被刷新修改,需要重新定位begin和endbreak;  //插入一个新值则跳出循环++beg;  //程序执行不到这里,没有存在的意义
}

怎么能往容器里面插入数据,且程序安全有序的运行,以下代码是连续插入多条数据的解决方案:

vector <int> myInt1 = { 100,200,300 };  //定义一个容器
auto beg = myInt1.begin();
int count = 0;while (beg != myInt1.end())
{beg = myInt1.insert(beg, count + 50);//在begin位置插入元素count++;if (count > 10){break;}beg++;
}vector <int> ::iterator iter;//定义一个迭代器
for (iter = myInt1.begin(); iter != myInt1.end(); iter++)//迭代器遍历容器的元素并打印
{cout << *iter << " ";
}

(2)灾难程序演示2

vector <int> myInt1 = { 100,200,300,400,500};  //定义一个容器
for (auto iter = myInt1.begin(); iter != myInt1.end();++iter)
{myInt1.erase(iter);
}vector <int> ::iterator iter;//定义一个迭代器
for (iter = myInt1.begin(); iter != myInt1.end(); iter++)//迭代器遍历容器的元素并打印
{cout << *iter << " ";
}

程序崩溃,原因是进行erase(iter)后,iter执向的是下一个元素的位置,导致在iter擦除元素500后,返回的是end的指向位置,但是end的位置不是有效的,所以程序会崩溃。

清除容器的元素,直接用clear直接清空容器的元素,如果用迭代器对容器内的元素一个一个的删除,一个简单直接且有效的方法如下:

vector <int> myInt1 = { 100,200,300,400,500};  //定义一个容器
while (!myInt1.empty())
{auto iter = myInt1.cbegin();myInt1.erase(iter);
}vector <int> ::iterator iter;//定义一个迭代器
for (iter = myInt1.begin(); iter != myInt1.end(); iter++)//迭代器遍历容器的元素并打印
{cout << *iter << " ";
}

七、范例演示

1、迭代器遍历字符串。

string str = "jinxueHou";for (auto iter = str.begin(); iter != str.end(); ++iter)
{cout << *iter;//一个一个字符串输出
}
cout << endl;

2、vector容器常用操作与内存释放。

(1)普通容器释放内存

vector <string> str;//定义一个string类型的容器str.push_back("12345678910");//往容器里面添加字符串
for (int i=0;i<1000000;i++)//一直往容器末尾添加数据
{str.push_back("12345678910");//push_back函数可能带来内存溢出
}cout << "clear前vector的容量为:" << str.capacity() << endl;
str.clear();
cout << "clear后vector的容量为:" << str.capacity() << endl;//先创建一个临时拷贝与原先的vector一致,值得注意的是,此时的拷贝其容量是尽可能小的符合所需数据的。
//紧接着将该拷贝与原先的vector v进行 交换。好了此时,执行交换后,临时变量会被销毁,内存得到释放。
//此时的v即为原先 的临时拷贝,而交换后的临时拷贝则为容量非常大的vector(不过已经被销毁)
vector <string>(str).swap(str);
cout << "swap后vector的容量为:" << str.capacity() << endl;或vector <string>().swap(str);

释放容器内存方法为:

vector<type>(v).swap(v);  或  vector<type>().swap(v);

type为容器数据类型,v是容器名称。

(2)指针元素容器内存释放

#include <iostream>
#include <vector>using namespace std;class student
{public:student(int num){this->num = num;cout << "num = " << this->num << endl;return;}~student(){cout << "内存析构调用" << endl;return;}int num;};int main()
{vector<student*> vstu;student* mystu1 = new student(100);student* mystu2 = new student(150);student* mystu3 = new student(200);student* mystu4 = new student(250);vstu.push_back(mystu1);vstu.push_back(mystu2);vstu.push_back(mystu3);vstu.push_back(mystu4);cout <<"clear之前容器容量:" << vstu.capacity() << endl;/*内存释放:程序员自己new就需要自己释放,否则会造成内存泄漏*///方式一for (auto &i:vstu)//必须执行这个步骤,否则析构函数占用的内存无法释放{delete i;//删除vstu元素}//方式二//vector<student*>::iterator vtemp;//for (vtemp = vstu.begin(); vtemp != vstu.end();vtemp++)//必须执行这个步骤,否则析构函数占用的内存无法释放//{// delete (*vtemp);//删除vstu元素//}vstu.clear();cout << "clear之后容器容量:" << vstu.capacity() << endl;vector<student*>().swap(vstu);cout << "swap之后容器容量: " << vstu.capacity() << endl;return 0;
}

到此,迭代器的操作和内存释放应用功能已完成。

2022.06.28结。

C++学习第八课--迭代器精彩演绎、失效分析及弥补、实战笔记相关推荐

  1. C语言学习第八课(EasyX图形库)

    第八课[注意:该图形库只能在c++文件下运行] 1,窗口函数 ·initgraph(int width,int height,int flag =NULL);//创建窗口 //width 指定窗口宽度 ...

  2. Redis学习第八课:Redis高级实用特性(一)

    Redis高级实用特性 注:我学习的环境是vmware7.1 + ubantu10.10+ redis 3.0.2 1.安全性 设置客户端连接后进行任何其他指定前需要的密码.因为redis速度相当快, ...

  3. java的构造特点_JAVA学习第八课(构造函数及其特点)

    构造函数: 构造函数,就是在构建 创造对象 时所调用的函数,作用就是给对象进行初始化 特点: 1.函数名和类名相同 2.不需要定义返回值类型 3.没有具体的返回值 作用:给对象进行初始化 注意: 1. ...

  4. 英语语法学习(一堂课让你懂得英语语法百分之九十五--笔记整理)

    视频链接(B站):https://www.bilibili.com/video/av47618740 原视频链接:http://v.baidu.com/watch/894088996105565168 ...

  5. 投资学习网课笔记(part8)--基金第八课

    学习笔记,仅供参考,有错必纠 文章目录 基金第八课 行业分类 医药行业指数基金 必须消费行业 行业的周期性 基金第八课 行业分类 摩根士丹利和标普在2000年推出了全球行业分类标准,将行业分为10个一 ...

  6. 第八课 k8s源码学习和二次开发原理篇-KubeBuilder使用和Controller-runtime原理

    第八课 k8s源码学习和二次开发原理篇-KubeBuilder使用和Controller-runtime原理 tags: k8s 源码学习 categories: 源码学习 二次开发 文章目录 第八课 ...

  7. 第八课 k8s网络基础学习-VxLAN基础

    第八课 k8s网络基础学习-VxLAN基础 tags: k8s网络 eNSP wireshark VxLAN 文章目录 第八课 k8s网络基础学习-VxLAN基础 第一节 VxLAN 1.1 VxLA ...

  8. Python学习第六课-列表

    Python学习第六课-列表 一.序列 1.1 概念 1.2分类 二.列表 2.1 概念 2.2 练习 三.切片 3.1 可切片对象的索引方式 3.2切片操作 3.3 练习 四.通用操作 4.1 序列 ...

  9. 打印循环换行_科学向日葵在线课堂 ——张老师讲Python 第八课 周而复始为循环2...

    点击蓝字关注我们 张老师讲编程--和爸爸妈妈一起学Python Python 的编辑器有很多,例如 PyCharm.Spyder.Notepad++等等,大家根据需要选择一个就好,初期程序代码量不大, ...

  10. 五年级上册计算机课如何拉表格,川教版小学信息技术五年级上册第八课 调整表格...

    第八课调整表格 教材分析: 本课是川教版小学<信息技术>第八课内容.学生已经通过前一节课的学习了掌握了在word中制作简单表格的基本方法,本课则是在前一节课的基础上,对表格进行调整与修饰, ...

最新文章

  1. 这是我见过最蛋疼的注册中心与API网关实践!
  2. java des验证码,Servlet返回验证码
  3. VTK:AngleWidget2D用法实战
  4. [SCOI 2010]传送带
  5. 不断开心的今天:Google Chrome Englive.cn iPhone Version。
  6. C++|Qt之QTcpServer基本用法
  7. 2019 下半年,程序员怎么过?
  8. 机器学习深度学习面试宝典-深度学习500问
  9. 计算机科学导论内容大纲,《计算机科学导论》大纲
  10. java运行vbs_如何在Java中执行VBS脚本?
  11. 高斯-马尔可夫定理(Gauss-Markov Theorem)
  12. iOS系统快捷指令一键打开北京健康宝个人信息扫码登记界面
  13. 《第一篇》二进制部署高可用K8S集群v1.24.2及运维(亲测无坑)
  14. 八芯网线水晶头做法(线序)
  15. 中国工商银行计算机专业笔试内容,中国工商银行的笔试一般考什么内容?
  16. 使用Latex画三线表时出现线条宽度不一致的情况
  17. 服务器ftp日志文件在哪里,ftp服务器的日志在哪
  18. 知乎社区平台分析报告
  19. codeforces 博弈 Arena of Greed
  20. 这本武林秘籍赶快收好

热门文章

  1. css样式中的border-radius属性
  2. UCOSIII---工程移植
  3. STM32单片机OLED经典2048游戏单片机小游戏
  4. 【3D建模制作技巧分享】3dmax如何设置视图布局
  5. 数据结构基础知识——非线性数据结构(二叉树、二叉排序树、优先队列、散列表)
  6. BT宝塔面板关闭强制绑定手机注册
  7. android pc游戏模拟器哪个好用,哪个电脑手游模拟器好用 安卓手游模拟器测试对比排行榜...
  8. android语法视频教程,英语语法视频讲解
  9. 元转万元单位换算_excel数值单位转换-----元与万元、千元、百元的转换
  10. ceph peering流程分析