zoukankan      html  css  js  c++  java
  • 迭代器容器C++标准模板库 迭代器 iterator 详解(二)

    发一下牢骚和主题无关:

        

        

    迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。迭代器就如统一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器不仅仅是指针,因此你不能认为他们一定具有地址值。例如,一个数组索引,也可以认为是一种迭代器。

        

    迭代器有各种不同的创立方法。程序可能把迭代器作为一个变量创立。一个STL容器类可能为了应用一个特定类型的数据而创立一个迭代器。作为指针,必须能够应用*操纵符类获得数据。你还可以应用其他数学操纵符如++。典范的,++操纵符用来递增迭代器,以访问容器中的下一个对象。如果迭代器到达了容器中的最后一个元素的前面,则迭代器酿成past-the-end值。应用一个past-the-end值得指针来访问对象是合法的,就好像应用NULL或为初始化的指针一样。

        

     

    #include <vector>
    #include <iostream>
    #include <list>
    #include <string>
    #include <set>
    #include <algorithm>
    #include <fstream>
    #include <iterator> //copy
    using namespace std;
    
    template <class T>
    inline void print (const T& coll, const char* optcstr="")
    {
        typename T::const_iterator pos;
    
        std::cout << optcstr;
        for (pos=coll.begin(); pos!=coll.end(); ++pos)
        {
            std::cout << *pos << ' ';
        }
        std::cout << std::endl;
    }
    
    void  test_back_inster()
    {
        vector<int> coll;
    
        // create back inserter for coll
        // - inconvenient way
        back_insert_iterator<vector<int> > iter(coll);
    
        // insert elements with the usual iterator interface
        *iter = 1991;
        iter++;
        *iter = 1992;
        iter++;
        *iter = 1993;
        print(coll);
    
        // create back inserter and insert elements
        // - convenient way
        back_inserter(coll) = 2013;
        back_inserter(coll) = 2014;
        print(coll,"use back_inserter\n");
    
        // use back inserter to append all elements again
        // - reserve enough memory to avoid reallocation
        coll.reserve(2*coll.size());
        copy (coll.begin(), coll.end(),    // source
              back_inserter(coll));        // destination
        print(coll,"after reserve and copy\n");
    }
    /********************
    运行结果:
    1991 1992 1993
    use back_inserter
    1991 1992 1993 2013 2014
    after reserve and copy
    1991 1992 1993 2013 2014 1991 1992 1993 2013 2014
    
    Process returned 0 (0x0)   execution time : 0.057 s
    Press any key to continue.
    
    **********************/
    void  test_front_insert()
    {
        list<string> coll;
    
        // create front inserter for coll
        // - inconvenient way
        front_insert_iterator<list<string> > iter(coll);
    
        // insert elements with the usual iterator interface
        *iter ="how";
        iter++;
        *iter = "are";
        iter++;
        *iter = "you";
    
        print(coll);
    
        // create front inserter and insert elements
        // - convenient way
        front_inserter(coll) = "I'm";
        front_inserter(coll) = "fine";
    
        print(coll,"use front_inserter(coll)\n");
    
        // use front inserter to insert all elements again
        copy (coll.begin(), coll.end(),    // source
              front_inserter(coll));       // destination
    
        print(coll,"after copy\n");
    }
    /****************************
    运行结果:
    you are how
    use front_inserter(coll)
    fine I'm you are how
    after copy
    how are you I'm fine fine I'm you are how
    
    Process returned 0 (0x0)   execution time : 0.021 s
    Press any key to continue.
    
    *****************************/
    void  test_insert()
    {
        set<int> coll;
    
        // create insert iterator for coll
        // - inconvenient way
        insert_iterator<set<int> > iter(coll,coll.begin());
    
        // insert elements with the usual iterator interface
        *iter = 1991;
        iter++;
        *iter = 1992;
        iter++;
        *iter = 1993;
    
        print(coll,"set of coll: \n");
    
        // create inserter and insert elements
        // - convenient way
        inserter(coll,coll.end()) = 2013;
        inserter(coll,coll.end()) = 2014;
    
        print(coll,"set of coll after insert:\n");
    
        // use inserter to insert all elements into a list
        list<int> coll2;
        copy (coll.begin(), coll.end(),           // source
              inserter(coll2,coll2.begin()));     // destination
    
        print(coll2,"list of coll2 after copy...inserter(coll2,coll2.begin()):\n");
    
        // use inserter to reinsert all elements into the list before the second element
        copy (coll.begin(), coll.end(),           // source
              inserter(coll2,++coll2.begin()));   // destination
    
         print(coll2,"list of coll2 after copy...inserter(coll2,++coll2.begin()):\n");
    }
    /********************************
    运行结果:
    set of coll:
    1991 1992 1993
    set of coll after insert:
    1991 1992 1993 2013 2014
    list of coll2 after copy...inserter(coll2,coll2.begin()):
    1991 1992 1993 2013 2014
    list of coll2 after copy...inserter(coll2,++coll2.begin()):
    1991 1991 1992 1993 2013 2014 1992 1993 2013 2014
    
    Process returned 0 (0x0)   execution time : 0.025 s
    Press any key to continue.
    
    **********************************/
    void test_ostream_iterator()
    {
        // create ostream iterator for stream cout
        // - values are separated by a newline character
        ostream_iterator<int> intWriter(cout,"\n");
    
        // write elements with the usual iterator interface
        *intWriter = -1949;
        intWriter++;
        *intWriter = -1978;
        intWriter++;
        *intWriter = -1911;
    
        // create collection with elements from 1 to 9
        vector<int> coll;
        for (int i=2007; i<=2013; ++i)
        {
            coll.push_back(i);
        }
    
        cout<<"\nwrite all elements without any delimiter\n";
        copy (coll.begin(), coll.end(),ostream_iterator<int>(cout));
        cout << endl;
    
        cout<<"\nwrite all elements without  delimiter space \n";
        copy (coll.begin(), coll.end(),ostream_iterator<int>(cout," "));
        cout << endl;
    
        cout<<"\nwrite all elements with  <  as delimiter\n";
        copy (coll.begin(), coll.end(),ostream_iterator<int>(cout," < "));
        cout << endl;
    
        cout<<"\n now test string :\n";
        vector<string> mystring;
        back_inserter(mystring)="please ";
        back_inserter(mystring)="believe in ";
        back_inserter(mystring)="your ";
        back_inserter(mystring)="future!";
        copy(mystring.begin(),mystring.end(),ostream_iterator<string>(cout));
    
    }
    /**********************************
    运行结果如下:
    -1949
    -1978
    -1911
    
    write all elements without any delimiter
    2007200820092010201120122013
    
    write all elements without  delimiter space
    2007 2008 2009 2010 2011 2012 2013
    
    write all elements with  <  as delimiter
    2007 < 2008 < 2009 < 2010 < 2011 < 2012 < 2013 <
    
     now test string :
    please believe in your future!
    Process returned 0 (0x0)   execution time : 0.062 s
    Press any key to continue.
    
    ************************************/
    void  test_istream_iterator_1()
    {
        // create istream iterator that reads integers from cin
        istream_iterator<int> intReader(cin);
    
        // create end-of-stream iterator
        istream_iterator<int> intReaderEOF;
    
        /* while able to read tokens with istream iterator
         * - write them twice
         */
        while (intReader != intReaderEOF)
        {
            cout << "you input is (should be int type):" << *intReader << endl;
            ++intReader;
        }
    
    }
    /***************************
    运行结果:
    1991
    you input is (should be int type):1991
    2003
    you input is (should be int type):2003
    2013
    you input is (should be int type):2013
    wangshihui
    
    Process returned 0 (0x0)   execution time : 19.581 s
    Press any key to continue.
    ****************************/
    void  test_istream_iterator_2()
    {
    
        ifstream readTheMisteryMethod( "TheMisteryMethod.txt",ios::app );//追加方式
        if(readTheMisteryMethod.is_open())
        {
            string str;
            cout<<"\nTheMisteryMethod.txt is as follow:\n";
            getline(readTheMisteryMethod ,str,'\0');//输出缓冲区剩余的字符
            cout<< str <<"\n";
            readTheMisteryMethod.close();
        }
        else
            cout << "Unable to open file\n";
    
        ifstream readTXT ( "TheMisteryMethod.txt",ios::app );//追加方式
        // Outputs to TheMisteryMethod.txt through readTXT
        if(readTXT.is_open())
        {
    
            istream_iterator<string> cinPos(readTXT);
            ostream_iterator<string> coutPos(cout," ");
    
            cout<<"\nwhile input is not at the end of the file\n";
            cout<<"write every third string\n\n";
             while (cinPos != istream_iterator<string>())
            {
                // ignore the following two strings
                advance (cinPos, 2);
    
                // read and write the third string
                if (cinPos != istream_iterator<string>())
                {
                    *coutPos++ = *cinPos++;
                }
            }
            cout << endl;
            readTXT.close();
        }
        else
            cout << "Unable to open file\n";
    }
    /*************************************
    运行结果:
    
    TheMisteryMethod.txt is as follow:
    To recap,the three main objectives in the Mystery Method are:
                To attract a woman
                To establish comfort, trust, and connection
                To structure the opportunity to be seduced
    
    while input is not at the end of the file
    write every third string
    
    three in Method attract To trust, To opportunity seduced
    
    Process returned 0 (0x0)   execution time : 0.026 s
    Press any key to continue.
    
    **************************************/
    int main()
    {
        return 0;
    }

        

        

     

        

    迭代器的类型

        

    对于STL数据结构和算法,你可以应用五种迭代器。下面简要说明白这五种类型:

        

    ·        Input iterators 提供对数据的只读访问。

        

    ·        Output iterators 提供对数据的只写访问

        

    ·        Forward iterators 提供读写操纵,并能向前推动迭代器。

        

    ·        Bidirectional iterators提供读写操纵,并能向前和向后操纵。

        每日一道理
    有些冷,有些凉,心中有些无奈,我一个人走在黑夜中,有些颤抖,身体瑟缩着,新也在抖动着,我看不清前方的路,何去何从,感觉迷茫,胸口有些闷,我环视了一下周围,无人的街头显得冷清,感到整个世界都要将我放弃。脚步彷徨之间,泪早已滴下……

        

    ·        Random access iterators提供读写操纵,并能在数据中随机挪动。

        

    尽管各种不同的STL实现细节方面有所不同,还是可以将上面的迭代器想象为一种类继承关系。从这个意义上说,下面的迭代器继承自上面的迭代器。由于这种继承关系,你可以将一个Forward迭代器作为一个output或input迭代器应用。一样,如果一个算法要求是一个bidirectional 迭代器,那么只能应用该种类型和随机访问迭代器。

        

        

        

        

        迭代器(iterator)是一种检查容器内元素并遍历元素的数据类型。

        (1) 每种容器类型都定义了自己的迭代器类型,如vector:

        vector<int>::iterator iter;这条语句定义了一个名为iter的变量,它的数据类型是由vector<int>定义的iterator类型。

        (2) 应用迭代器读取vector中的每个元素:

        vector<int> ivec(10,1);

        for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();++iter)

        {

        *iter=2; //应用 * 访问迭代器所指向的元素

        }

        const_iterator:

        只能读取容器中的元素,而不能修改。

        for(vector<int>::const_iterator citer=ivec.begin();citer!=ivec.end();citer++)

        {

        cout<<*citer;

        //*citer=3; error

        }

        vector<int>::const_iterator 和 const vector<int>::iterator的区分

        const vector<int>::iterator newiter=ivec.begin();

        *newiter=11; //可以修改指向容器的元素

        //newiter++; //迭代器本身不能被修改 

        (3) iterator的算术操纵:

        iterator除了进行++,--操纵,可以将iter+n,iter-n赋给一个新的iteraor对象。还可以应用一个iterator减去另外一个iterator.

        const vector<int>::iterator newiter=ivec.begin();

        vector<int>::iterator newiter2=ivec.end();

        cout<<"\n"<<newiter2-newiter;

        

        STL是C++中重要部分之一(面向对象、STL、模板等),其中三个基本的STL组件包括:
    1. 迭代器。迭代器之于容器相当于指针之于数组,提供了访问容器对象的方法,事实上C++中的指针也是一种迭代器,但是要注意迭代器不仅仅是指针,不一定具有地址值。
    2. 容器。容器是一种模板类,例如list、vector、deques等,一般由迭代器访问容器中的数据。
    3. 算法。STL中数据结构和算法是分离的,各种函数在狭义容器中(包括链表、数组、string对象、容器)完整通用。

        1.头文件:
    STL头文件一般不应用.h扩展,其中重要应用的头文件和对应容器类如下:
    #include Container Class
    <deque> deque
    <list> list
    <map> map, multimap
    <queue> queue, priority_queue
    <set> set, multiset
    <stack> stack
    <vector> vector
    <string> string
    <iterator>各种 iterator
    <algorithm> 各种算法函数
    STL均应用标准命名空间using namespace std。

        2.迭代器:
    迭代器有五种类型,这五种类型是一种继承关系,详细如下:
    Input iterators:提供对数据的只读访问,前向推动。输入迭代器可以应用==和!=来测试是否相等;应用*来访问数据;应用++操纵符前向推动。例如find函数需要保证输入迭代器。
    Output iterators:提供对数据的只写访问,前向推动。输出迭代器缺省只写,由于该迭代器无法读取对象,因此不会在任何搜索和其他算法中应用它。
    Forward iterators:提供读写访问,前向推动。例如replace函数需要保证前向迭代器。
    Bidirectional iterators:提供读写访问,前向或后向推动。例如reverse函数需要保证双向迭代器。
    Random access iterators:提供读写访问,随机挪动(非const的指针也是随机迭代器)。STL中的排序和搜索函数应用随机访问迭代器,随机访问迭代器可以应用关系操纵符做比拟。
    除此之外,还包括一些特别的迭代器:
    指针迭代器:一个指针也是一种迭代器。
    常量迭代器:对于只读变量,为了防止错误赋值,可以应用常量迭代器const_iterator,该迭代器指向的对象不允许转变。注意:const ***<***>::iterator的含意是该迭代器成为常量,不可指向其他数据,与常量迭代器的含意是不一样的。

        3.流迭代器
    将输入输出(例如标准输入输出流cin/cout或者文件流等)作为容器看待,因此接受迭代器参数的算法都可以和流一同工作。
    STL定义模板类ostream_iterator作为输出流迭代器,其构造函数有两个参数,包括一个ostream对象和一个string值(作为距离符),因此可以象下面一样创立一个迭代器:
    ostream_iterator<int>(cout, “\t”)             //定义cout迭代器
    ofstream out(“text.txt”);
    ostream_iterator<string> obegin(out, “\n”);       //定义文件流输出迭代器
    STL定义模板类istream_iterator作为输入流迭代器,可指定读取的来源,并应该和结束迭代器比拟。详细如下:
    istream_iterator<int> intreader(cin);       //定义cin流迭代器
    isteam_iterator<int> eof;
    copy(istream_iterator<string>(cin), istream_iterator<string>(), 输出迭代器);      //定义无变量名的cin流迭代器
    ifstream in(“text.txt”);
    istream_iterator<string> ibegin(in);
    istream_iterator<string> iend;            //定义文件流输入迭代器
    还有一些详细应用如下:

        //利用流迭代器填充vector
    {
        ifstream in("test.txt");
        istream_iterator<string> ibegin(in);
        istream_iterator<string> iend;
        vector<string> vec(ibegin, iend);
        copy(vec.begin(), vec.end(), ostream_iterator<string>(cout, "\n"));
    }
    //利用输入流填充vector
    {
        vector<string> vec;
        copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(vec));
        sort(vec.begin(), vec.end());
        copy(vec.begin(), vec.end(), ostream_iterator<string>(cout,"\n"));
    }
    //利用流迭代器保存vector内容到文件
    {
        ifstream in("test.txt");
        istream_iterator<string> ibegin(in);
        istream_iterator<string> iend;
        vector<string> vec(ibegin, iend);
        ofstream out("testcopy.txt");
        copy(vec.begin(), vec.end(), ostream_iterator<string>(out, "\n"));    
    }

        注意:上面用输入流迭代器来初始化vector后,不可再用这个输入流迭代器,因为随着数据的读取,迭代器已到达输入流或者文件流的末端了。

        4.插入迭代器:
    int arr[] = {1, 2, 3, 4, 5};
    vector<int> vi;
    copy(arr, arr + 5; vi.begin());
    该语句不会执行,因为没无为vi分配存储空间。此时应用插入迭代器可以将值插入到容器中,自动为vi扩展存储空间,重要包括三种插入迭代器。
    普通插入器:将对象插入到容器任何对象的前面。该迭代器应用容器的insert操纵符替代赋值运算符,第一个参数是容器本身,第二个参数是容器迭代器指定插入位置。
    Front inserters:将对象插入到数据集的前面,例如链表表头。该迭代器应用push_front操纵替代赋值运算符,参数是容器本身。
    Back inserters:将对象插入到数据集的尾部,例如vector的尾部,导致vector容器扩展。该迭代器调用容器的push_back操纵替代赋值运算符,参数是容器本身。
    注意:应用插入迭代器可能导致容器中的其他对象挪动位置,因此现有的迭代器酿成合法,需要重新赋值(list除外,不受影响)。

        int arr[] = {1, 2, 3, 4, 5};
    vector<int> vi;
    copy(arr, arr + 5; front_iterator(vi));
    //最终结果按序是5 4 3 2 1,因为每次调用push_front将一个数据插入到vi的前面。
    Vector<int>::iterator p = find(vi.begin(), vi.end(), 2);
    copy(arr, arr + 2, inserter(vi, p));
    //最终结果是5 4 3 1 2 2 1,因为调用insert一次性将全部数据插入到p前。

        
    5.混合迭代器函数:
    下面两个迭代器函数非常有用:
    advance(iterator, int):按照指定的数目增减迭代器。第一个参数是迭代器,第二个参数是增减的数目(前向迭代器该数必须为正,双向或者随机迭代器该数可认为负)。
    distance(iterator, iterator, int&):返回到达一个迭代器所需递增操纵的数目。该函数是递归的,每次递归第三个参数,因此必须初始化该参数为0然后应用该函数。

        

        

        五种迭代器之间的关系
        vector 和deque提供的是RandomAccessIterator,list提供的是BidirectionalIterator,set和map提供的 iterators是 ForwardIterator,关于STL中iterator迭代器的操纵如下:
    说明:每种迭代器都可进行包括表中前一种迭代器可进行的操纵。
    迭代器操纵                      说明
    (1)全部迭代器
    p++                              后置自增迭代器
    ++p                              前置自增迭代器
    (2)输入迭代器
    *p                                 复引用迭代器,作为右值
    p=p1                             将一个迭代器赋给另一个迭代器
    p==p1                           比拟迭代器的相等性
    p!=p1                            比拟迭代器的不等性
    (3)输出迭代器
    *p                                 复引用迭代器,作为左值
    p=p1                             将一个迭代器赋给另一个迭代器
    (4)正向迭代器
    提供输入输出迭代器的全部功能
    (5)双向迭代器
    --p                                前置自减迭代器
    p--                                后置自减迭代器
    (6)随机迭代器
    p+=i                              将迭代器递增i位
    p-=i                               将迭代器递加i位
    p+i                                在p位加i位后的迭代器
    p-i                                 在p位减i位后的迭代器
    p[i]                                返回p位元素偏离i位的元素引用
    p<p1                             如果迭代器p的位置在p1前,返回true,否则返回false
    p<=p1                           p的位置在p1的前面或统一位置时返回true,否则返回false
    p>p1                             如果迭代器p的位置在p1后,返回true,否则返回false
    p>=p1                           p的位置在p1的前面或统一位置时返回true,否则返回false   

        

        

    插入迭代器

        插入迭代器(inserter iterator)是一个可以访问序列容器vector<T>、deque<T>和list<T>添加新元素的迭代器。有3个创立插入迭代器的模板:

        Back_insert_iterator<T>在类型T的容器末端插入元素。容器必须提供push_back()函数。

        Front_insert_iterator<T>在类型T的容器开头插入元素。一样push_front()对要求可用。

        Insert_iterator<T>在类型T的容器内从指定位置开始插入元素。这要求容器有一个insert()函数,此函数接受两个参数,迭代器作为第一个实参,要插入的项作为第二个实参。

        前两个插入迭代器类型的构造函数接受一个指定要在其中插入元素的容器的实参。如:

    list<int> numbers;
    front_insert_iterator<list<int> > iter(numbers);

    向容器中插入值:

    *iter = 99;

    也可以将front_inserter()函数用于numbers容器:

    front_inserter(numbers) = 99;

    这几行代码为numbers列表创立了一个前段插入器,并用它在开头插入99。front_inserter()函数的实参是运用迭代器的容器。

        insert_iterator<T>迭代器的构造函数需要两个实参:

    insert_iterator<vector<int> > iter(numbers,numbers.begin());

    该构造函数的第二个实参是一个指定在何处插入数据的迭代器。向此容器赋值:

    for (int i = 0; i < 100; i++)
        *iter = i + 1;

    代码执行后,前100个元素的值依次为100,99,…,1。

        

    输出流迭代器

        为了弥补输入流迭代器模板,ostream_iterator<T>模板提供了向输出流写类型T的对象的输出流迭代器。

    ostream_iterator<int> out(cout);

    该模板的实参int指定要处置的数据类型,构造函数实参cout指定将作为数据的目的地的流,以便cout迭代器能将int类型的值写到标准输出流中。如:

    int data [] = {1,2,3,4,5,6,7,8,9};
    vector<int> numbers(data,data+9);
    copy(numbers.begin(),numbers.end(),out);

    在algorithm头文件中定义的copy()算法将由前两个迭代器实参指定的对象序列复制到第三个实参指定的输出迭代器。此代码执行结果为:123456789.

        但现在写到标准输出流中的值没有空格。第二个输出流迭代器构造函数能处理这一点:

    ostream_iterator<int> out(cout,” ”);

    现在将输出1 2 3 4 5 6 7 8 9

        

        

    文章结束给大家分享下程序员的一些笑话语录: 人工智能今天的发展水平:8乘8的国际象棋盘其实是一个体现思维与创意的强大媒介。象棋里蕴含了天文数字般的变化。卡斯帕罗夫指出,国际象棋的合法棋步共有1040。在棋局里每算度八步棋,里面蕴含的变化就已经超过银河系里的繁星总数。而地球上很少有任何数量达到这个级别。在金融危机之前,全世界的财富总和大约是1014人民币,而地球人口只有1010。棋盘上,所有可能的棋局总数达到10120,这超过了宇宙里所有原子的总数!经典语录网

  • 相关阅读:
    已解决[Authentication failed for token submission,Illegal hexadecimal charcter s at index 1]
    远程快速安装redis和远程连接
    远程快速安装mysql
    Swiper的jquery动态渲染不能滑动
    微服务架构攀登之路(三)之gRPC入门
    微服务架构攀登之路(二)之RPC
    微服务架构攀登之路(一)之微服务初识
    Go语言中new和make的区别
    Go语言实战爬虫项目
    Go语言系列(十一)- 日志收集系统架构
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3087256.html
Copyright © 2011-2022 走看看