一、迭代器简介
迭代器是一种遍历容器内元素的数据类型,这种数据类型有点像指针,理解时候可以理解为迭代器用来指向容器中的某个元素。
vector可以用[]来访问,但是更常用的访问方式是用迭代器。
通过迭代器,我们可以读容器中的元素值,读string中的每个字符,还可以修改某个迭代器所指向的元素值。
一般有容器都会有相关的迭代器list、map等。
二、容器的迭代器类型
vector <int> iv = {100,200,300};
vector<int>::iterator iter;//定义迭代器,也必须是vector<int>
理解时候可以把整个vector::iterator理解成一个类型,这个类型专门应用于迭代器。
当我们用这个类型定义一个变量时,这个变量就是个迭代器,这里iter这个变量就是个迭代器
三、迭代器begin()/end()操作,反向迭代器rbegin()/rend()操作
begin()/end()用来返回迭代类型.rbegin()/rend()一样。
iter = iv.begin();//如果容器中有元素则begin返回的迭代器指向容器中第一个元素
iter = iv.end();//end返回末尾元素的后面一个元素
如果是一个空容器,那么begin()和end()返回的迭代器就相同。
用迭代器来遍历容器
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> value{1,2,3,4};
for (vector<int>::iterator iter = value.begin(); iter != value.end(); iter++)
{
cout << *iter << endl;
}
return 0;
}
结果
1
2
3
4
逆向迭代器:从后往前访问
rbegin():返回一个反向迭代器指向反向迭代器的第一个元素
rend();返回一个反向迭代器指向反向迭代器的最后一个元素的下一个位置(容器最开始元素的前一个位置)
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> value{1,2,3,4};
for (vector<int>::reverse_iterator riter = value.rbegin(); riter != value.rend(); riter++)
{
cout << *riter << endl;
}
return 0;
}
结果
4
3
2
1
四、迭代器运算符
*iter:返回迭代器iter所指向元素的引用。必须要保证这个迭代器指向是有效的容器元素,不能指向end(),因为end()是末端元素后边(不存在的一个元素)riter同理。
++iter;iter++;–iter;iter–;都遵循以上原则(不能指向不存在的元素)
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
struct student
{
int num;
};
int main()
{
vector<student> sv;
student mystu;
mystu.num = 100;
sv.push_back(mystu);//把对象mystu复制到sv容器中
vector<student>::iterator iter;
iter = sv.begin();//指向第一个元素
cout << (*iter).num << endl;
cout << iter->num << endl;
return 0;
}
100
100
五、const_iterator迭代器
const_iterator迭代器表示迭代器指向的值不能改变,只能从迭代器中读元素。(iterator能读能写)
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> iv{1,2,3,4};
vector<int>::const_iterator iter;
for (iter = iv.begin(); iter != iv.end(); iter++)
{
cout << *iter << endl;
}
return 0;
}
结果
1
2
3
4
如果是const vector iv{1,2,3,4};常量容器的话就必须用const_iterator来迭代。
5.1cbegin()和cend()操作
c++11引入的两个新函数cbegin()和cend(),跟begin,end类似。cbegin,cend,返回的都是常量迭代器
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> iv{1,2,3,4};
for (auto iter = iv.cbegin(); iter != iv.cend(); iter++)
{
cout << *iter << endl;
}
return 0;
}
与上面结果一样,既然返回的是常量迭代器那么与const_iterator一样不能进行赋值操作。
六、迭代器失效
回顾一下学习容器时的失效问题
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> iv{1,2,3,4};
for (auto vecitem: iv)
{
iv.push_back(88);
cout << vecitem << endl;
}
return 0;
}
/*
for(auto beg = iv.begin(), end = iv.end();beg != end; ++beg)
{
iv.push_back(88);
cout << *beg <<endl;
}
*/
结果
1
-572662307
-572662307
-572662307
注:在迭代器遍历时进行增/删操作与注释内容是等价的,在操作迭代器过程中,千万不要改变vector容器容量,也就是不要进行增删操作。增加或者删除操作可能会使指向容器的元素指针、引用、迭代器失效。程序可能直接崩溃。如果要进行增加或者删除操作要立即退出去。
for (auto beg = iv.begin(), end = iv.end(); beg != end; ++beg)
{
iv.push_back(88);
break;
}
for (auto beg = iv.begin(), end = iv.end(); beg != end; ++beg)
{
iv.push_back(88);
cout << *beg << endl;
}
如果想一直插入一定要做好处理,最好是做完操作立即break;
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> iv{1,2,3,4};
auto beg = iv.begin();
int icount=0;
while (beg != iv.end())//每次更新end防止end失效
{
beg = iv.insert(beg, icount++);//insert参数1插入位置,参数2插入元素
if (icount > 10)
break;
++beg;
}
beg = iv.begin();
auto end = iv.end();
while (beg != end)
{
cout << *beg << endl;
++beg;
}
return 0;
}
0
1
2
3
4
5
6
7
8
9
10
1
2
3
4
在程序结束前我们可能会释放掉容器里面内容看如下代码(错误的方法)
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> iv{1,2,3,4};
auto beg = iv.begin();
for (auto iter = iv.begin(); iter != iv.end(); ++iter)
{
iv.erase(iter);//erase:移除iter位置上的元素,返回下一个元素位置
}
return 0;
}
当这段程序执行时直接崩溃,不用想,问题出在迭代器上,正确做法如下
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> iv{1,2,3,4};
auto iter = iv.begin();
while (iter != iv.end())
{
iter = iv.erase(iter);
}
/*
while (!iv.empty())//判断是否为空
{
auto iter = iv.begin();//因为不为空,所以返回begin没问题
iv.erase(iter);//删除位置上的元素
}
*/
return 0;
}
以上两种方法是一个一个清空,直接清空也可以使用iv.clear();方法
七、范例演示
7.1、用迭代器变量一下string类型数据
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
int main()
{
string str("I Love China!");
for (auto iter = str.begin(); iter != str.end(); ++iter)
{
*iter = toupper(*iter);//转成大写
}
cout << str << endl;
return 0;
}
I LOVE CHINA!
7.2vector容器常用操作与内存释放
#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;
struct conf
{
char itemname[40];
char itemcontent[100];
};
char *getinfo(vector<conf *>&conflist, char *pitem)//返回字符串
{
for (auto iter = conflist.begin(); iter != conflist.end(); ++iter)
{
if (_stricmp((*iter)->itemname, pitem) == 0)//根据名字返回内容
{
return (*iter)->itemcontent;
}
}
return nullptr;//没有找到返回空
}
int main()
{
//ServerName = 1区//表示服务器名称
//ServerID =1000//表示服务器ID
conf *pconf1 = new conf;
strcpy_s(pconf1->itemname, sizeof(pconf1->itemname), "ServerName");
strcpy_s(pconf1->itemcontent, sizeof(pconf1->itemcontent), "1区");
conf *pconf2 = new conf;
strcpy_s(pconf2->itemname, sizeof(pconf2->itemname), "ServerID");
strcpy_s(pconf2->itemcontent, sizeof(pconf2->itemcontent), "1000");
vector<conf *> conflist;
conflist.push_back(pconf1);
conflist.push_back(pconf2);
char *p_temp = getinfo(conflist, "ServerName");
if (p_temp != nullptr)
{
cout << p_temp << endl;//1区
}
//自己new的自己释放,否则会有内存泄漏
std::vector<conf *>::iterator pos;
for(pos = conflist.begin(); pos != conflist.end(); ++pos)
{
delete (*pos);//删除图中蓝色内存,容器里面指向的内存无效了
}
conflist.clear();//列表指针指向的内存释放掉了,把列表也清空一下
return 0;
}