迭代器是一种遍历容器内元素的数据类型。这种数据类型感觉有点像指针,读者就理解为迭代器是用来指向容器中的某个元素的。
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迭代器类型,当用这个类型定义一个变量时,这个变量就是一个迭代器变量。
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更像一个常量指针。如下例子可以更改容器里面的元素值:
- 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和end
- break; //插入一个新值则跳出循环
- ++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结。