第二章 基本语言
第九节 迭代器精彩演绎,失效分析及弥补、实战
1. 迭代器简介
-
迭代器是一种遍历容器内元素的数据类型,这种数据类型有点像指针,我们理解为迭代器用来指向容器中的某个元素
-
string, vector,[], 很少用[],更常用的访问方式就是用迭代器(更通用)
-
通过迭代器,我们可以读容器中的元素值,读string中的每个字符,还可以修改某个迭代器所指向的元素值
-
++/–
-
list, map,尽量学会用迭代器来访问容器中的元素
2. 容器的迭代器类型
vector<int> vint{100, 200, 233};
// 把整个vector<int>::iterator 理解成一个类型,这种类型专门应用于迭代器
// 当我们用这个类型定义一个变量的时候,这个变量就是迭代器,这里的iter这个变量就是个迭代器
vector<int>::iterator iter;//定义迭代器,也必须是vector<int>
3. 迭代器begin()/end()操作,反向迭代器rbegin()/rend()操作
-
begin()/end()用来返回迭代类型, rbegin()/rend()也是用来返回迭代类型
-
begin():如果容器中有元素,则begin()返回的迭代器,指向的是容器的第一个元素
-
end():返回一个迭代器类型
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> vint{100, 200, 233};
vector<int>::iterator iter;//定义迭代器
// iter指向了vint[0]
iter = vint.begin();
// end返回的迭代器指向的并不是末端元素,而是末端元素后边
// 可以理解成end()指向的是一个不存在的元素
iter = vint.end();
return 0;
}
- 如果容器为空的话,begin()和end()返回的迭代器相同
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> vint{};
vector<int>::iterator iterb, itere;//定义迭代器
iterb = vint.begin();
itere = vint.end();
if(iterb==itere){
cout << "容器为空!" << endl;
}
return 0;
}
- 传统写法
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> vint{1, 3, 393};
for (vector<int>::iterator iter = vint.begin(); iter != vint.end(); iter++) {
cout << *iter << endl;
}
return 0;
}
-
反向迭代器: 想从后往前遍历一个容器,那么使用反向迭代器就比较方便
-
rbegin():返回反向迭代器,返回反向迭代器的第一个元素
-
rend(): 返回一个反向迭代器,返回反向迭代器的最后元素的下一个位置
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> vint{1, 3, 393};
for (vector<int>::reverse_iterator riter = vint.rbegin(); riter != vint.rend(); ++riter) {
cout << *riter << endl;
}
return 0;
}
4. 迭代器运算符
-
*iter:返回迭代器iter所指向元素的引用,必须要保证这个迭代器指向的是有效的容器元素,
不能指向end(),因为end()是末端元素的后边,即是一个不可控的位置 -
++iter,iter++: 让迭代器指向容器中的下一个元素,已经指向end()的时候,不能再进行自加
-
–iter, iter–:让迭代器指向容器中的上一个元素,已经指向begin()的时候,不能再进行自减
-
iter1 == iter2, iter1 != iter2,判断两个迭代器是否相等,如果两个迭代器指向的是同一个元素,就相等,否则就不相等
-
容器中是结构体,如何引用结构中的成员
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct student {
int num;
};
int main() {
vector<student> sv;
student stu;
stu.num = 100;
sv.push_back(stu);
vector<student>::iterator iter;
iter = sv.begin();
cout << (*iter).num << endl;
cout << iter->num << endl;
return 0;
}
5. const_iterator迭代器
-
const: 表示常量
-
const_iterator迭代器,表示迭代器指向的元素值不能改变,指向的位置可以改变
所以这个迭代器只能从容器中读元素,不能改写容器中的元素,更像一个常量指针
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
vector<int>::const_iterator citer;
for(citer = iv.begin(); citer != iv.end(); citer++){
// 会报错
//*citer = 1;
cout << *citer <<endl;//可以正常读
}
return 0;
}
5.1 cbegin()和cend()操作
- c++ 引入的两个新函数cbegin(),cend()跟begin()和end()类似,cbegin()和cend返回的都是常量迭代器
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
for( auto iter = iv.cbegin(); iter != iv.cend(); iter++){
// 会报错,因为返回的是常量迭代器
//*iter = 1;
cout << *iter <<endl;//可以正常读
}
return 0;
}
6. 迭代器失效
-
在操作迭代器的过程中,不要改变容器的容量,也就是不要增加或者删除vector容器中的元素
-
在容器中增加或者从容器中删除元素,这些操作可能会使指向容器元素的指针,引用,迭代器失效,
失效就表示不能再代表容器中的元素,一旦使用失效的对象,程序就很容易崩溃
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
for (auto beg = iv.begin(); beg != iv.end(); beg++) {
// 导致程序崩溃
iv.push_back(1);
cout << *beg << endl;
}
return 0;
}
- 解决方案,如果一定要插入一个数据,加入break,然后重新开启一个循环
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
for (auto beg = iv.begin(); beg != iv.end(); beg++) {
// 导致程序崩溃
iv.push_back(1);
break;
}
for (auto beg = iv.begin(); beg != iv.end(); beg++) {
cout << *beg <<endl;
}
return 0;
}
6.1 灾难程序演示1
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
auto beg = iv.begin();
auto end = iv.end();
while (beg != end) {
cout << *beg << endl;
//插入新值
//加入想往begin这个位置插入新值第一个参数为插入的位置,第二个参数为插入的元素
//会导致迭代器失效,具体哪部分失效,依赖于容器的内部实现,最好的做法是break出来
iv.insert(beg,88);
break;//最明智的防止迭代器失效的方法,否则程序很可能崩溃
++beg;
}
auto beg1 = iv.begin();
auto end1 = iv.end();
while (beg1 != end1) {
cout << *beg1 << endl;
++beg1;
}
return 0;
}
- 保证迭代器不失效的代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
auto beg = iv.begin();
// 每次更新end(),防止end迭代器失效
int icount = 0;
while (beg != iv.end()) {
// insert的返回结果用beg接收
beg = iv.insert(beg, icount + 10);
icount++;
if (icount > 5) {
break;
}
++beg;
}
auto beg1 = iv.begin();
auto end1 = iv.end();
while (beg1 != end1) {
cout << *beg1 << endl;
++beg1;
}
return 0;
}
6.2 灾难程序演示2
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
//...
for (auto iter = iv.begin(); iter != iv.end(); ++iter) {
// earse函数,移除iter位置上的元素,返回下一个元素位置
iv.erase(iter);//迭代器失效,导致程序崩溃
}
return 0;
}
- 修改代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
//...
vector<int>::iterator iter = iv.begin();
while(iter != iv.end()){
iter = iv.erase(iter);
}
return 0;
}
- 针对vector,一个一个删除元素的方法
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<int> iv{100, 200, 300};
while(!iv.empty()){
auto beg = iv.begin();//因为不为空,所以返回begin()是没问题的
iv.erase(beg);//删除该位值元素
}
return 0;
}
7. 范例演示
7.1 用迭代器遍历一下string类型数据
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
string str{"I am Felaim!"};
for (auto iter = str.begin(); iter != str.end(); ++iter) {
*iter = toupper(*iter);
}
cout << str << endl;
return 0;
}
7.2 vector容器常用操作与内存释放
实战程序
ServerName = 1区 //表示服务器名称
ServerID =88 // 表示服务器ID
- 完整实例
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
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 (strcmp((*iter)->itemname, pitem) == 0) {
return (*iter)->itemcontent;
}
}
}
int main() {
// new出来的内存要释放!!!
conf *pconf1 = new conf;
strcpy(pconf1->itemname, "ServerName");
strcpy(pconf1->itemcontent, "1区");
conf *pconf2 = new conf;
strcpy(pconf2->itemname, "ServerID");
strcpy(pconf2->itemcontent, "1000");
vector<conf *> conflist;
conflist.push_back(pconf1);
conflist.push_back(pconf2);
// for (auto iter = conflist.begin(); iter != conflist.end(); ++iter) {
// cout << (*iter)->itemname << endl;
// cout << (*iter)->itemcontent << endl;
// }
char *p_tmp = getinfo(conflist, "ServerName");
if (p_tmp != nullptr) {
cout << p_tmp << endl;
}
// 需要释放内存
vector<conf *>::iterator pos;
for (pos = conflist.begin(); pos != conflist.end(); ++pos) {
//*pos才是对应的指针,并没有破坏迭代器,释放的是迭代器指向的额外内存
delete ((*pos));
}
conflist.clear();
return 0;
}