1. 程式人生 > 其它 >迭代器(第4章)(條款26,27,28,29)

迭代器(第4章)(條款26,27,28,29)

技術標籤:《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中文版》