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

特别说明:作者写本书的时候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中文版》

猜你喜欢

转载自blog.csdn.net/u010323563/article/details/112757610