迭代器精彩演绎,失效分期及弥补、实战
一、迭代器简介
迭代器是一种遍历容器内元素的数据类型。这种数据类型感觉有点像指针。
string,vector,[],很少用[],更通用的方式就是用迭代器。
通过迭代器,我们可以读容器中的元素值,读string中的每个字符,还可以修改某个迭代器所指向的元素值。
迭代器支持++,--自增,自减操作,相关list/map容器
二、容器的迭代器类型
vector<int> iv(100,200,300); vector<int>::iterator iter; // 定义迭代器,也必须是vector<int>
三、迭代器的begin()/end(),反向迭代器rbegin()/rend()操作
begin()/end()用来返回迭代类型,rbegin()/rend()返回迭代类型
(1)begin()返回一个迭代器类型
iter = iv.begin();// 如果容器中有元素,则begin返回的迭代器,指向容器中的第一个元素。 即iter指向了iv[0]
(2)end():返回迭代器类型
iter = iv.end(); // end返回的迭代器指向的并不是末端元素,而是末端元素的后边
(3)如果一个容器为空,则begin()/end()返回的迭代器相同
vector<int> iv2; vector<int>::iterator iterbegin = iv2.begin(); vector<int>::iterator iterend = iv2.end(); if(iterbegin == iterend) { cout << “容器iv2为空” << endl; }
(4)传统迭代器的写法
vector<int> iv(100,200,300); for(vector<int>::iterator iter = iv.begin();iter != iv.end(); iter++) { cout << *iter << endl; // 依次打印100,200,300 }
(5)反向迭代器
反向迭代器:从后往前遍历容器中的元素
反向迭代器(逆向迭代器),用的rbegin(),rend()
rbegin()返回一个反向迭代器,指向反向迭代器的第一个元素
rend()返回一个反向迭代器,指向反向迭代器的最后一个元素的下一个位置
vector<int> iv(100,200,300); for(vector<int>::reverse_iterator riter = iv.rbegin(); riter != iv.rend(); riter++) { cout << *riter << endl; // 依次打印300,200,100 }
四、迭代器运算符
(1)*iter:返回迭代器iter所指向元素的引用。必须保证这个迭代器指向的是有效的容器元素,不能指向end(),因为end()是末端后面的元素,即end()是指向一个不存在的元素。
vector<int> iv(100,200,300); //vector<int>::iterator iter = iv.end(); // 存在问题 vector<int>::iterator iter = iv.begin(); cout << *iter << endl;
(2)++iter,iter++:让迭代器指向容器的下一个元素;已经指向end()时候,不能再自加,自减。
//vector<int>::iterator iter = iv.end(); // 存在问题 vector<int>::iterator iter = iv.begin(); iter++; cout << *iter << endl;
(3)--iter,iter--:让迭代器指向容器中的上一个元素。指向开头元素,不能再--
//vector<int>::iterator iter = iv.begin(); // 存在问题 vector<int>::iterator iter = iv.end(); iter--; cout << *iter << endl;
(4)iter1 == iter2; iter1 != iter2 判断两个迭代器是否相等。
如果两个迭代器指向的是同一个元素,就相等,否则就不等。
(5)如何引用结构中的成员
struct student { int num; }; 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;
五、const_iterator迭代器,const:常量
const_iterator迭代去,表示值不能改变的意思,这里的值不能改变表示这个迭代器指向的元素的值不能改变,而不是表示这个迭代器本身不能改变,即迭代器本身是可以不断指向下一个元素;只能从容器中读元素,不能通过这个迭代器改写容器中的元素,感觉起来更像常量指针。
const vector<int> iv = {100,200,300}; vector<int>::const_iterator iter; for(iter = iv.begin(); iter != iv.end(); iter++) { //*iter = 4; // 出错,不可修改 cout << *iter << endl; }
(1)cbegin()/cend()操作
C++11引入的两个新函数,跟begin,end类似;cbegin,cend,返回的都是常量迭代器
for(auto iter = iv.cbegin(); iter != iv.cend(); iter++) { //*iter = 4; // 报错,不能给常量赋值,这说明cbegin返回的是常量迭代器。 cout << *iter << endl; }
六、迭代器失效
vector<int> vecvalue{1,2,3,4,5}; for(auto vecitem : vecvalue) { //vecvalue.push_back(444); // 报错,显示结果混乱 cout << vecitem << endl; } for(auto beg = vecvalue.begin(); beg != vecvalue.end(); beg++) { //vecvalue.push_back(444); // 报错,崩溃 cout << *beg << endl; }
在操作迭代器的过程中,使用迭代器这种循环体,千万不要改变vector容器的容量。即不要增加或删除vector容器中的容量。
向容器中增加元素或删除元素,这些操作可能会使指向容器元素的指针,引用,迭代器失效,失效表示不能再代表任何容器中的元素。一旦使用失效的东西,就等于犯了严重的程序错误,很多情况下,程序会直接崩溃。
for(auto beg = vecvalue.begin(); beg != vecvalue.end(); beg++) { vecvalue.push_back(444); break; // 插入新元素直接跳出 } for(auto beg = vecvalue.begin(); beg != vecvalue.end(); beg++) { cout << *beg <<endl; }
(1)灾难程序1
vector<int> vecvalue{1,2,3,4,5}; auto beg = vecvalue.begin(); auto end = vecvalue.end(); while(beg != end) { cout << *beg << endl; // 加入想往begin这个位置插入新值,可以用insert vecvalue.insert(beg,80); //插入新值,第一个参数为插入位置,第二个参数为插入值。 // 这个值的插入,会使迭代器失效。比如begin,end失效 // 具体哪个迭代器失效,取决于vector内部的实现原理。 // 最明智的做法就是立即break跳出循环,否则程序会崩溃。 break; beg++; // 不要忘记,并且放在循环末尾 } beg = vecvalue.begin(); end = vecvalue.end(); while(beg != end) { cout << *beg << endl; beg++; // 不要忘记,并且放在循环末尾 }
// 迭代器插入防止迭代器失效 vector<int> vecvalue{1,2,3,4,5}; auto beg = vecvalue.begin(); auto end = vecvalue.end(); int icount = 0; while(beg != vecvalue.end()) // 每次更新end防止end失效 { beg = vecvalue.insert(beg,icount+80); icount++; if(icount>10) break; ++beg; } // 遍历迭代器里面的数据 beg = vecvalue.begin(); end = vecvalue.end(); while(beg != end) { cout << *beg << endl; beg++; // 不要忘记,并且放在循环末尾 }
(2)灾难程序2 – 容器的释放问题
vector<int> iv = {100,200,300}; //… for(auto iter = iv.begin();iter != iv.end();iter++) { // erase函数,移除iter位置上的元素,返回下一个元素位置 iv.erase(iter); // 存在问题,直接崩溃 } // 稳定版容器释放 vector<int> iter = iv.begin(); while(iter != iv.end()) { iter = iv.erase(iter); } // 更简单的释放 while(!iv.empty()) { auto iter = iv.begin();// 因为不为空,所以返回的begin()是没问题 iv.erase(iter); // 删除该位置上的元素 }
七、范例演示
(1)用迭代器编译string类型数据
string str(“I Love China”); for(auto iter = str.begin();iter != str.end();++iter) { *iter = toupper(*iter); } cout << str << endl;
(2)vector容器常用操作及内存释放
迭代器实战例子
// ServerName = 1区 // 表示服务器名称 // ServeID = 100000 // 服务器ID struct conf { char itemname[40]; // 项目名 itemContext[100]; // 项目内容 }; // 根据项目名查询项目内容 char *getinfo(vector<conf *> &conflict,const char *pitem) { for(auto pos = conflict.begin(); pos != conflict.end(); ++pos) { if(_strcmp((*pos)->itemname,pitem) == 0) { return (*pos)->itemcontext; } } return nullptr; } int main() { conf *pconf1 = new conf; strcpy_s(pconf1->itemname,sizeof(pconf1->itemname),”ServerName”); strcpy_s(pconf1->itemcontext,sizeof(pconf1->itemcontext),”1区”); conf *pconf2 = new conf; strcpy_s(pconf2->itemname,sizeof(pconf2->itemname),” ServeID”); strcpy_s(pconf2->itemcontext,sizeof(pconf2-> itemcontext),” 100000”); vector<conf *> conflist; conflict.push_back(conf1);// [0] conflict.push_back(conf2);// [1] //strcpy_s(pconf1->itemname,sizeof(pconf1->itemname),”ServerName123”); // 查询项目名字对应的内容 char *p_tmp = getinfo(conflict,”ServerName”); if(p_item != nullptr) { cout << p_item << endl; } // 释放内存 std::vector<conf *>::iterator pos; for(pos = conflict.begin(); pos != conflict.end(); pos++) { delete (*pos); // *pos代表了conf的指针,删除自己new出来的内存 } conflict.clear(); // 这个要不要都行。 return 0; }