第二章 基本語言
第九節 迭代器精彩演繹,失效分析及彌補、實戰
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;
}