迭代器(第4章)(條款26,27,28,29)
阿新 • • 發佈:2021-01-18
技術標籤:《Effective STL》讀書筆記c++stl
特別說明:作者寫本書的時候C++11標準還沒有釋出,不同編譯器廠商對C++標準的實現有些差異,導致現在去看書的部分內容和最新的C++標準以及大部分編譯器的行為有些差異,請特別注意。
條款26:iterator優先於const_iterator, reverse_iterator, const_reverse_iterator
書中提到的例子,我進行了測試,除了連續儲存的序列容器insert/erase成員函式的引數不能為reverse_iterator外,其他均可以,以及iterator與const_iterator之間的比較、算術運算等。
void iteratorTest1() { vector<int> vec{0, 0, 1, 2, 3, 4}; // erase和insert成員函式中對迭代器型別的要求 vec.erase(vec.begin()); // right vec.erase(vec.cbegin()); // right // vec.erase(vec.rbegin()); // ERROR reverse iterator 不能作為erase的引數 vec.insert(vec.end(), 5); // right, vec{1,2,3,4,5} vec.insert(vec.cend(), 6); // right, vec{1,2,3,4,5,6} // vec.insert(vec.rbegin(), 5); // ERROR reverse iterator 不能作為insert的引數 // iterator 與 const_iterator 比較 auto iter = vec.begin(); auto citer = vec.cbegin(); if (iter == citer || citer == iter) { // 為啥兩個不同的迭代器型別可以比較呢,應該是iterator隱式轉換為const_iterator了 // 實質上是兩個const_iterator之間的比較 cout << "iterator can be compared with const_iterator" << endl; } std::advance(iter, 3); // 將iter向前累加3個位置 if (iter - citer >= 3 || citer + 3 <= iter) { // iterator與const_iterator之間能進行算術運算 cout << "iterator can be operator with const_iterator" << endl; } return; }
條款27:使用distance和advance將容器的const_iterator轉換為iterator
現代C++大部分編譯器都可直接轉換,不需要條款中那麼複雜的做法,但是書中的做法肯定是具有可移植性的
void iteratorTest2() { // vector/string可以 vector<int> vec{1, 2, 3, 4, 5}; auto citer1 = vec.cbegin(); auto iter1 = citer1; // 可以轉換 cout << *iter1 << endl; std::advance(iter1, std::distance(iter1, citer1)); // 也OK,但是沒必要這麼複雜,上面的直接轉換就可以 cout << *iter1 << endl; // deque/list/set/map/unordered_set等 std::deque<int> dq{1, 2, 3, 4, 5}; auto citer2 = dq.cbegin(); auto iter2 = citer2; // 可以轉換 cout << *iter2 << endl; std::advance(iter2, std::distance(iter2, citer2 + 2)); // 也OK,但是沒必要這麼複雜,上面的直接轉換就可以 cout << *iter2 << endl; std::list<int> ls{1, 2, 3, 4, 5}; auto citer3 = ls.cbegin(); auto iter3 = citer3; // 可以轉換 cout << *iter3 << endl; std::advance(iter3, std::distance(iter3, citer3)); // 也OK,但是沒必要這麼複雜,上面的直接轉換就可以 cout << *iter3 << endl; std::map<int, int> mp{{1, 2}, {2, 3}}; auto citer4 = mp.cbegin(); auto iter4 = citer4; // 可以轉換 cout << iter4->first << ", " << iter4->second << endl; std::advance(iter4, std::distance(iter4, citer4)); // 也OK,但是沒必要這麼複雜,上面的直接轉換就可以 cout << iter4->first << ", " << iter4->second << endl; std::unordered_set<int> ust{1,2,3,4,5}; auto citer5 = ust.cbegin(); auto iter5 = citer5; cout << *iter5 << endl; // 可以轉換 std::advance(iter5, std::distance(iter5, citer5)); // 也OK,但是沒必要這麼複雜,上面的直接轉換就可以 cout << *iter5 << endl; // 下面是條款中介紹的做法,這個做法肯定具有可移植性,但是稍顯複雜 using ConstIter = std::unordered_set<int>::const_iterator; std::advance(iter5, std::distance<ConstIter>(iter5, citer5)); return; }
條款28:正確理解reverse_iterator的base()成員函式所產生的iterator的用法
void iteratorTest3() {
vector<int> vec{1, 2, 3, 4, 5};
auto riter1 = std::find(vec.rbegin(), vec.rend(), 3);
cout << "*riter1: " << *riter1 << endl; // 3
auto iter1 = riter1.base();
cout << "*iter1: " << *iter1 << endl; // 4
auto riter2 = vec.rbegin() + 2;
cout << "*riter2: " << *riter2 << endl; // 3
auto iter2 = riter2.base();
cout << "*iter2: " << *iter2 << endl; // 4
// 插入資料,行為是一致的
// vec.insert(riter2, 99); // 實際上不能用reverse_iterator作為引數,這兒假設能插入,則結果入下{1, 2, 3, 99, 4, 5}
iter2 = vec.insert(iter2, 99); // vec = {1, 2, 3, 99, 4, 5}
cout << *iter2 << endl;
// 如果是刪除資料,則行為不一致,需要特別注意,這個時候兩者不是指向同一個元素
auto riter3 = vec.rbegin() + 2;
cout << "*riter3: " << *riter3 << endl; // 99
auto iter3 = riter3.base();
cout << "*iter3: " << *iter3 << endl; // 4
// vec.erase(--riter3.base()); // 成功刪除99, 但是某些編譯器可能無法通過編譯,因為C++規定從函式返回的指標不應該被修改
// vec.erase(--iter3); // 成功刪除99
vec.erase((++riter3).base()); // 書中推薦的做法,成功刪除99
return;
}
條款29:對於逐字元的輸入請考慮使用istreambuf_iterator
istream_iterator是使用operator>>從輸入流中讀取單個字元,涉及許多格式化操作,預設情況下函式會跳過空白字元;
istreambuf_iterator則是直接從流的緩衝區中讀取下一個字元,無格式化,不會跳過任何字元;
void iteratorTest4() {
// istream_iterator
// 從輸入流中讀取單個字元
// 預設使用operator>>,涉及格式化等操作,效率不高,而且會忽略空白字元
std::ifstream ifs1;
ifs1.open("EnData.txt");
std::string fileData1;
if (ifs1.is_open()) {
fileData1.append((std::istream_iterator<char>(ifs1)), std::istream_iterator<char>());
}
// istreambuf_iterator
// 適用於非格式化的逐個字元輸入過程
// 直接從流的緩衝區中讀取下一個字元,效率更高,且不會跳過任何字元
std::ifstream ifs2;
ifs2.open("EnData.txt");
std::string fileData2;
if (ifs2.is_open()) {
fileData2.append((std::istreambuf_iterator<char>(ifs2)), std::istreambuf_iterator<char>());
}
return;
}
除錯截圖
參考:《Effective STL中文版》