zoukankan      html  css  js  c++  java
  • C++笔记------迭代器

    STL是一种泛型编程。对象编程关注的是编程的数据方面,泛型编程关注的是算法的通用,它们之间的共同点的抽象和创建可重用代码,但理念不同。

    STL使用术语“概念”描述迭代器所需要满足的一系列要求,如正向迭代器是一种要求,而不是类型。

    STL使用术语“改进”来表示这种概念上的继承,概念具有类似继承的关系,但不能将C++继承机制用于迭代器。

    概念的具体实现被称为模型,所以一个指向int的常规指针是一个随机访问迭代器的模型,也是一个正向迭代器的模型。

    STL首先为每个容器类定义了相应的迭代器类型,迭代器可以是指针,也可以是对象。迭代器都将提供一些基础操作,如*和++。

      其次每个容器类都有超尾标记,通过让迭代器++操作,从第一个元素逐步指向超尾位置,实行遍历容器中每一个元素。

    STL定义了5种迭代器,分别为输入迭代器输出迭代器正向迭代器双向迭代器随机访问迭代器。这5种迭代器都可以执行解除应用操作,如果两个迭代器相同,则对它们执行解除引用得到的值也一样。迭代器的特征和容器的特征是基于算法要求进行设计。

      1.输入迭代器:

      (1)“输入”是指从容器中获取信息,所以输入迭代器用来读取容器中信息,但不能让程序修改值。

      (2)输入迭代器是单向的,可以使用++运算符进行递增操作,但不能倒退。

      2.输出迭代器:

      (1)“输出”是指信息从程序中传输给容器,所以只能让程序修改容器的值,但不能读取。

      (2)输出迭代器也是单向的,可以使用++运算符进行递增操作,但不能倒退。

    Ps:对于单通行,只写的算法,可以使用输出迭代器;对于单通行,只读的算法,可以使用输入迭代器。

      3.正向迭代器:

      (1)正向迭代器只使用++运算符来遍历容器,能按相同的顺序进行遍历。

      (2)正向迭代器递增后,仍可以对前面的迭代器(已经保存的)值进行解除引用操作,可以得到相同的值。

      (3)正向迭代器既可以读取和修改数据,也可以只读数据。

      4.双向迭代器:

      (1)包含所有正向迭代器的特性,支持递减运算符(a--和--a)。

      5.随机迭代器:

      (1)能够直接跳到容器中任何一个元素,进行随机访问,包含了所有双向迭代器的特性。

    Ps:

      1.编写算法时,通过使用最低级别的迭代器,可以增加算法的通用性。例如fing()函数可以用于任何包含可读取的迭代器的容器。而sort()函数需要随机访问迭代器,所以只有支持这种迭代器的容器。

      2.各种迭代器的类型并不是确定的,只是一种概念性描述。每个容器类定义一个类级的typedef 名称-----iterator。

    如前所说,迭代器是广义指针,而指针满足迭代器所有要求,因此STL算法可以使用对基于指针的非STL容器进行操作。

    例如:  

      C++将超尾概念用于数组,可以将STL的算法用于常规数组。指针也是迭代器,也使得STL算法用于常规数组。

    #include <iostream>
    #include <iterator>
    #include <algorithm>
    #include <vector>
    using namespace std;
    
    void output(const int s) { cout << s << " "; }
    
    int main()
    {
        int casts[10] = { 6,7,2,9,4,11,8,7,10,5 };
        std::vector<int> dice(10);
        //将数据从一种容器复制到另一种容器 
        //前两个参数复制范围,最后一个参数复制到什么位置。 
        //目标容器必须足够大,以便能容纳元素 
        copy(casts, casts + 10, dice.begin());
        for (auto x : casts) cout << x << " ";//基于范围的for循环 
        cout << endl;
        ostream_iterator<int, char>out_iter(cout, " ");
                                              //适配器——一个类或函数,可以将其它接口转换成STL使用的接口
                                              //第一个参数:指出发送给输出流的数据类型
                                              //第二个参数:指出输出流使用的字符类型(可能是wchar_t)
                                              //构造函数的第一个参数:指出使用的输出流
                                              //第二个参数:输出流的每个数据项后面显示的分隔符 
        copy(dice.begin(), dice.end(), out_iter);
        cout << endl;
        *out_iter++ = 15; //将15和空格组成的字符串发送到cout管理的输出流中    想当于 cout << 15 << " "; 
        cout << endl;
        //可以直接使用,而不创建命名迭代器 
        copy(dice.begin(), dice.end(), ostream_iterator<int, char>(cout, " "));
        std::vector<int> dice1(10);
        vector<int> ::iterator rp;
        for (rp = dice1.begin(); rp != dice1.end(); rp++)
            cin >> *rp;
        for_each(dice1.begin(), dice1.end(), output);
        cout << endl;
        vector<int>::reverse_iterator ri; //对 revere_iterator执行递增操作会导致它自减 
                                          //rbegin() and end() 返回值一样,但类型不一样(reverse_iterator and iterator)
                                          //rend() and begin() 返回值一样,类型不一样,都返回指向第一元素的地址 
                                          //在对ri进行解除引用之前,先自减,在解除引用。例如:ri执行位置6,*ri是位置5的数值 
        for (ri = dice1.rbegin(); ri != dice1.rend(); ++ri)
            cout << *ri << " ";
        cout << endl;
        return 0;
    }

    Ps:

      1.为什么ri需要先自减?

        因为rbegin()返回超尾,所以不能进行解除引用,同理rend()返回的是第一个元素地址,所以必须提早一个位置停止(区间的结尾不包括在区间内)。

    除了以上已用迭代器,还有以下三种插入迭代器:

      back_insert_iterator:将元素插入容器尾部中

      front_insert_iterator:将元素插入容器前端

      insert_iterator:将元素插入指定的位置前面

    以上三种迭代器可以提高STL算法通用性,可以将容器类型作为模版参数。例如:

      back_insert_iterator<vector<int> > back_iter(dice)

      insert_iterator<vector<int> > insert_iter(dice,dice.begin() );

    #include <iostream>
    #include <vector>
    #include <iterator>
    #include <algorithm>
    #include <string>
    
    void output(const std::string &s) { std::cout << s << " "; }
    
    using namespace std;
    int main()
    {
        string s1[4] = { "fine" , "fish","fashion","fate" };
        string s2[3] = { "I" , "am" , "Zhang" };
        string s3[2] = { "Zeze" , "good" };
        vector<string> words(4);
        copy(s1,s1 + 4,words.begin());
        for_each(words.begin(),words.end(),output);
        cout << endl;
        copy(s2,s2 + 2,back_insert_iterator<vector<string> >(words));
        for_each(words.begin(),words.end(),output);
        cout << endl;
        copy(s3,s3 + 2,insert_iterator<vector<string> >(words,words.begin()));
        for_each(words.begin(),words.end(),output);
        cout << endl;
        return 0;
    }

    Ps: 

      1.copy()是没有调整容器大小的权限,迭代器必须使用适合的容器方法,back_insert_iterator<vector<string > >back_iter(dice);可以让back_iter使用vector<int>::push_back(),该方法可以调整容器大小。

      2.容器vector没有front_insert_iterator迭代器,因为vector没有push_front()。

  • 相关阅读:
    就为了一个原子操作,其他CPU核心罢工了
    浅谈JVM和垃圾回收
    简单了解一下K8S,并搭建自己的集群
    WebAssembly完全入门——了解wasm的前世今身
    【简单了解系列】从基础的使用来深挖HashMap
    【俗话说】换个角度理解TCP的三次握手和四次挥手
    两分钟让你明白Go中如何继承
    游戏服务器和Web服务器的区别
    Go中使用seed得到相同随机数的问题
    从web到游戏,走出舒适区
  • 原文地址:https://www.cnblogs.com/zhangzeze/p/8868868.html
Copyright © 2011-2022 走看看