小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

泛型算法 (輸入輸出迭代器和算法綜合介紹)

 無(wú)云666 2015-02-19

第十一章 泛型算法

標(biāo)準(zhǔn)庫(kù)容器定義的操作非常少。標(biāo)準(zhǔn)庫(kù)沒有給容器添加大量的功能函數(shù),而是選擇提供一組算法,這些算法大都不依賴特定的容器類型,是“泛型”的,可作用在不同類型的容器和不同類型的元素上。

考慮下面的例子,可以使用 accumulate 把 string 型的 vector 容器中的元素連接起來(lái):

     // concatenate elements from v and store in sum

     string sum = accumulate(v.begin(), v.end(), string(""));

這個(gè)函數(shù)調(diào)用的效果是:從空字符串開始,把 vec 里的每個(gè)元素連接成一個(gè)字符串。注意:程序顯式地創(chuàng)建了一個(gè) string 對(duì)象,用該函數(shù)調(diào)用的第三個(gè)實(shí)參。傳遞一個(gè)字符串字面值,將會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤。因?yàn)榇藭r(shí),累加和的類型將是 const char*,而 string 的加法操作符(第 3.2.3 節(jié))所使用的操作數(shù)則分別是 string 和 const char* 類型,加法的結(jié)果將產(chǎn)生一個(gè) string 對(duì)象,而不是 const char* 指針。

對(duì)指定數(shù)目的元素做寫入運(yùn)算,或者寫到目標(biāo)迭代器的算法,都不檢查目標(biāo)的大小是否足以存儲(chǔ)要寫入的元素。

copy 帶有三個(gè)迭代器參數(shù):頭兩個(gè)指定輸入范圍,第三個(gè)則指向目標(biāo)序列的一個(gè)元素。傳遞給 copy 的目標(biāo)序列必須至少要與輸入范圍一樣大。假設(shè) ilst 是一個(gè)存放 int 型數(shù)據(jù)的 list 對(duì)象,可如下將它 copy 給一個(gè) vector 對(duì)象:

     vector<int> ivec; // empty vector

     // copy elements from ilst into ivec

     copy (ilst.begin(), ilst.end(), back_inserter(ivec));

replace 算法就是一個(gè)很好的例子。該算法對(duì)輸入序列做讀寫操作,將序列中特定的值替換為新的值。該算法帶有四個(gè)形參:一對(duì)指定輸入范圍的迭代器和兩個(gè)值。每一個(gè)等于第一值的元素替換成第二個(gè)值。

     // replace any element with value of 0 by 42

     replace(ilst.begin(), ilst.end(), 0, 42);

這個(gè)調(diào)用將所有值為 0 的實(shí)例替換成 42。如果不想改變?cè)瓉?lái)的序列,則調(diào)用 replace_copy。這個(gè)算法接受第三個(gè)迭代器實(shí)參,指定保存調(diào)整后序列的目標(biāo)位置。

     // create empty vector to hold the replacement

     vector<int> ivec;

     // use back_inserter to grow destination as needed

     replace_copy (ilst.begin(), ilst.end(), back_inserter(ivec), 0, 42);

假設(shè)我們的輸入存儲(chǔ)在一個(gè)名為 words 的 vector 對(duì)象中,第一個(gè)子問(wèn)題是將 words 中重復(fù)出現(xiàn)的單詞去除掉:

     // sort words alphabetically so we can find the duplicates

     sort(words.begin(), words.end());

     /* eliminate duplicate words:

      * unique reorders words so that each word appears once in the

      *    front portion of words and returns an iterator one past the unique range;

      * erase uses a vector operation to remove the nonunique elements

      */

     vector<string>::iterator end_unique =

                    unique(words.begin(), words.end());

     words.erase(end_unique, words.end());

單詞按次序排列后,現(xiàn)在的問(wèn)題是:讓故事中所用到的每個(gè)單詞都只保留一個(gè)副本。unique 算法很適合用于解決這個(gè)問(wèn)題,它帶有兩個(gè)指定元素范圍的迭代器參數(shù)。該算法刪除相鄰的重復(fù)元素,然后重新排列輸入范圍內(nèi)的元素,并且返回一個(gè)迭代器,表示無(wú)重復(fù)的值范圍的結(jié)束。

After the call to unique, the vector holds

調(diào)用 unique 后,vector 中存儲(chǔ)內(nèi)容是:

注意,words 的大小并沒有改變,依然保存著 10 個(gè)元素;只是這些元素的順序改變了。調(diào)用 unique“刪除”了相鄰的重復(fù)值。給“刪除”加上引號(hào)是因?yàn)?nbsp;unique 實(shí)際上并沒有刪除任何元素,而是將無(wú)重復(fù)的元素復(fù)制到序列的前端,從而覆蓋相鄰的重復(fù)元素。unique 返回的迭代器指向超出無(wú)重復(fù)的元素范圍末端的下一位置。

現(xiàn)在此 vector 對(duì)象已經(jīng)按單詞長(zhǎng)度排序,剩下的問(wèn)題就是統(tǒng)計(jì)長(zhǎng)度不小于 6 的單詞個(gè)數(shù)。使用 count_if 算法處理這個(gè)問(wèn)題:

     vector<string>::size_type wc =

                  count_if(words.begin(), words.end(), GT6);

     bool GT6(const string &s)

     {

         return s.size() >= 6;

     }

盡管這個(gè)函數(shù)能解決問(wèn)題,但存在不必要限制——函數(shù)內(nèi)部硬性規(guī)定了對(duì)長(zhǎng)度大小的要求。如果要統(tǒng)計(jì)其他長(zhǎng)度的單詞個(gè)數(shù),則必須編寫另一個(gè)函數(shù)。其實(shí)很容易寫出更通用的比較函數(shù),使它帶有兩個(gè)形參,分別是 string 對(duì)象和一個(gè)長(zhǎng)度大小值即可。但是,傳遞給 count_if 算法的函數(shù)只能帶有一個(gè)實(shí)參,因此本程序不能使用上述更通用的方法。第 14.8.1 節(jié)將為這個(gè)問(wèn)題提供更好的解決方案。

了解程序的細(xì)節(jié)之后,下面是完整的程序:

     // comparison function to be used to sort by word length

     bool isShorter(const string &s1, const string &s2)

     {

         return s1.size() < s2.size();

     }

     // determine whether a length of a given word is 6 or more

     bool GT6(const string &s)

     {

         return s.size() >= 6;

     }

     int main()

     {

         vector<string> words;

         // copy contents of each book into a single vector

         string next_word;

         while (cin >> next_word) {

             // insert next book's contents at end of words

             words.push_back(next_word);

         }

         // sort words alphabetically so we can find the duplicates

         sort (words.begin(), words.end());

         /* eliminate duplicate words:

          * unique reorders words so that each word appears once in the

          *       front portion of words and returns an iterator one past the unique range;

          * erase uses a vector operation to remove the nonunique elements

          */

         vector<string>::iterator end_unique =

                     unique(words.begin(), words.end());

         words.erase(end_unique, words.end());

          // sort words by size, but maintain alphabetic order for words of the same size

          stable_sort(words.begin(), words.end(), isShorter);

          vector<string>::size_type wc =

                          count_if (words.begin(), words.end(), GT6);

          cout << wc << " " << make_plural(wc, "word", "s")

               << " 6 characters or longer" << endl;

          return 0;

     }

 

 

反向迭代器:這類迭代器實(shí)現(xiàn)向后遍歷,而不是向前遍歷。所有容器類型都定義了自己的 reverse_iterator 類型,由 rbegin 和 rend 成員函數(shù)返回。

第 11.2.2 節(jié)已強(qiáng)調(diào)標(biāo)準(zhǔn)庫(kù)所定義的迭代器不依賴于特定的容器。事實(shí)上,C++ 語(yǔ)言還提供了另外三種迭代器:

插入迭代器:這類迭代器與容器綁定在一起,實(shí)現(xiàn)在容器中插入元素的功能。

iostream 迭代器:這類迭代器可與輸入或輸出流綁定在一起,用于迭代遍歷所關(guān)聯(lián)的 IO 流。

反向迭代器:這類迭代器實(shí)現(xiàn)向后遍歷,而不是向前遍歷。所有容器類型都定義了自己的 reverse_iterator 類型,由 rbegin 和 rend 成員函數(shù)返回。

上述迭代器類型都在 iterator 頭文件中定義。

 

11.3. Revisiting Iterators

11.3. 再談迭代器

In Section 11.2.2 (p. 398) we saw that the library defines iterators that are independent of a particular container. In fact, there are three additional kinds of iterators:

第 11.2.2 節(jié)已強(qiáng)調(diào)標(biāo)準(zhǔn)庫(kù)所定義的迭代器不依賴于特定的容器。事實(shí)上,C++ 語(yǔ)言還提供了另外三種迭代器:

insert iterators: These iterators are bound to a container and can be used to insert elements to the container.

插入迭代器:這類迭代器與容器綁定在一起,實(shí)現(xiàn)在容器中插入元素的功能。

·  

iostream iterators: These iterators can be bound to input or output streams and used to iterate through the associated IO stream.

iostream 迭代器:這類迭代器可與輸入或輸出流綁定在一起,用于迭代遍歷所關(guān)聯(lián)的 IO 流。

·  

reverse iterators: These iterators move backward, rather than forward. Each container type defines its own reverse_iterator types, which are retuned by the rbegin and rend functions.

反向迭代器:這類迭代器實(shí)現(xiàn)向后遍歷,而不是向前遍歷。所有容器類型都定義了自己的 reverse_iterator 類型,由 rbegin 和 rend 成員函數(shù)返回。

These iterator types are defined in the iterator header.

上述迭代器類型都在 iterator 頭文件中定義。

This section will look at each of these kinds of iterators and show how they can be used with the generic algorithms. We'll also take a look at how and when to use the container const_iterators.

本節(jié)將詳細(xì)分析上述每種迭代器,并介紹在泛型算法中如何使用這些迭代器,還會(huì)了解什么時(shí)候應(yīng)該使用和如何使用 const_iterator 容器

 

11.3.1. Insert Iterators

11.3.1. 插入迭代器

第 11.2.2 節(jié)使用 back_insert 創(chuàng)建一個(gè)迭代器,用來(lái)給容器添加元素。back_inserter 函數(shù)是一種插入器。插入器是一種迭代器適配器(第 9.7 節(jié)),帶有一個(gè)容器參數(shù),并生成一個(gè)迭代器,用于在指定容器中插入元素。通過(guò)插入迭代器賦值時(shí),迭代器將會(huì)插入一個(gè)新的元素。C++ 語(yǔ)言提供了三種插入器,其差別在于插入元素的位置不同。

back_inserter,創(chuàng)建使用 push_back 實(shí)現(xiàn)插入的迭代器。

front_inserter,使用 push_front 實(shí)現(xiàn)插入。

inserter,使用 insert 實(shí)現(xiàn)插入操作。除了所關(guān)聯(lián)的容器外,inserter 還帶有第二實(shí)參:指向插入起始位置的迭代器。

 

上述迭代器類型都在 iterator 頭文件中定義。

inserter 適配器提供更普通的插入形式。這種適配器帶有兩個(gè)實(shí)參:所關(guān)聯(lián)的容器和指示起始插入位置的迭代器。

     // position an iterator into ilst

     list<int>::iterator it =

      find (ilst.begin(), ilst.end(), 42);

     // insert replaced copies of ivec at that point in ilst

     replace_copy (ivec.begin(), ivec.end(),

                   inserter (ilst, it), 100, 0);

首先用 find 定位 ilst 中的某個(gè)元素。使用 inserter 作為實(shí)參調(diào)用 replace_copy,inserter 將會(huì)在 ilst 中由 find 返回的迭代器所指向的元素前面插入新元素。而調(diào)用 replace_copy 的效果是從 ivec 中復(fù)制元素,并將其中值為 100 的元素替換為 0 值。ilst 的新元素在 it 所標(biāo)明的元素前面插入。

也許我們會(huì)認(rèn)為可使用 inserter 和容器的 begin 迭代器來(lái)模擬 front_inserter 的效果。然而,inserter 的行為與 front_inserter 的有很大差別。在使用 front_inserter 時(shí),元素始終在容器的第一個(gè)元素前面插入。而使用 inserter 時(shí),元素則在指定位置前面插入。即使此指定位置初始化為容器中的第一個(gè)元素,但是,一旦在該位置前插入一個(gè)新元素后,插入位置就不再是容器的首元素了:

     list<int> ilst, ilst2, ilst3;     // empty lists

     // after this loop ilst contains: 3 2 1 0

     for (list<int>::size_type i = 0; i != 4; ++i)

          ilst.push_front(i);

     // after copy ilst2 contains: 0 1 2 3

     copy (ilst.begin(), ilst.end(), front_inserter(ilst2));

     // after copy, ilst3 contains: 3 2 1 0

     copy (ilst.begin(), ilst.end(),

                  inserter (ilst3, ilst3.begin()));

在復(fù)制并創(chuàng)建 ilst2 的過(guò)程中,元素總是在這個(gè) list 對(duì)象的所有元素之前插入。而在復(fù)制創(chuàng)建 ilst3 的過(guò)程中,元素則在 ilst3 中的固定位置插入。剛開始時(shí),這個(gè)插入位置是此 list 對(duì)象的頭部,但插入一個(gè)元素后,就不再是首元素了。

istream_iterator<T> in(strm);

創(chuàng)建從輸入流 strm 中讀取 T 類型對(duì)象的 istream_iterator 對(duì)象

istream_iterator<T> in;

istream_iterator 對(duì)象的超出末端迭代器

ostream_iterator<T> in(strm);

創(chuàng)建將 T 類型的對(duì)象寫到輸出流 strm 的 ostream_iterator 對(duì)象

 ostream_iterator<T> in(strm, delim);

創(chuàng)建將 T 類型的對(duì)象寫到輸出流 strm 的 ostream_iterator 對(duì)象,在寫入過(guò)程中使用 delim 作為元素的分隔符。delim 是以空字符結(jié)束的字符數(shù)組

 

流迭代器只定義了最基本的迭代器操作:自增、解引用和賦值。此外,可比較兩個(gè) istream 迭代器是否相等(或不等)。而 ostream 迭代器則不提供比較運(yùn)算(表 11.2)。

Table 11.2. istream_iterator Operations

表 11.2. istream_iterator 的操作

it1 == it2 it1 != it2

比較兩上 istream_iterator 對(duì)象是否相等(不等)。迭代器讀取的必須是相同的類型。如果兩個(gè)迭代器都是 end 值,則它們相等。對(duì)于兩個(gè)都不指向流結(jié)束位置的迭代器,如果它們使用同一個(gè)輸入流構(gòu)造,則它們也相等

*it

返回從流中讀取的值

it->mem

是 (*it).mem 的同義詡。返回從流中讀取的對(duì)象的 mem 成員

++it it++

通過(guò)使用元素類型提供的 >> 操作從輸入流中讀取下一個(gè)元素值,使迭代器向前移動(dòng)。通常,前綴版本使用迭代器在流中向前移動(dòng),并返回對(duì)加 1 后的迭代器的引用。而后綴版本使迭代器在流中向前移動(dòng)后,返回原值

 

流迭代器的定義

流迭代器都是類模板:任何已定義輸入操作符(>> 操作符)的類型都可以定義 istream_iterator。類似地,任何已定義輸出操作符(<< 操作符)的類型也可定義 ostream_iterator。

在創(chuàng)建流迭代器時(shí),必須指定迭代器所讀寫的對(duì)象類型:

     istream_iterator<int> cin_it(cin);    // reads ints1 from cin

     istream_iterator<int> end_of_stream;  // end iterator value

     // writes Sales_items from the ofstream named outfile

     // each element is followed by a space

     ofstream outfile;

     ostream_iterator<Sales_item> output(outfile, " ");

ostream_iterator 對(duì)象必須與特定的流綁定在一起。在創(chuàng)建 istream_iterator 時(shí),可直接將它綁定到一個(gè)流上。另一種方法是在創(chuàng)建時(shí)不提供實(shí)參,則該迭代器指向超出末端位置。ostream_iterator 不提供超出末端迭代器。

在創(chuàng)建 ostream_iterator 對(duì)象時(shí),可提供第二個(gè)(可選的)實(shí)參,指定將元素寫入輸出流時(shí)使用的分隔符。分隔符必須是 C 風(fēng)格字符串。因?yàn)樗?nbsp;C 風(fēng)格字符串,所以必須以空字符結(jié)束;否則,其行為將是未定義的。

istream_iterator 對(duì)象上的操作

構(gòu)造與流綁定在一起的 istream_iterator 對(duì)象時(shí)將對(duì)迭代器定位,以便第一次對(duì)該迭代器進(jìn)行解引用時(shí)即可從流中讀取第一個(gè)值。

考慮下面例子,可使用 istream_iterator 對(duì)象將標(biāo)準(zhǔn)輸入讀到 vector 對(duì)象中。

     istream_iterator<int> in_iter(cin); // read ints from cin

     istream_iterator<int> eof; // istream "end" iterator

     // read until end of file, storing what was read in vec

     while (in_iter != eof)

             // increment advances the stream to the next value

             // dereference reads next value from the istream

             vec.push_back(*in_iter++);

更有趣的是可以這樣重寫程序:

     istream_iterator<int> in_iter(cin); // read ints from cin

     istream_iterator<int> eof;      // istream "end" iterator

     vector<int> vec(in_iter, eof);  // construct vec from an iterator range

這里,用一對(duì)標(biāo)記元素范圍的迭代器構(gòu)造 vec 對(duì)象。這些迭代器是 istream_iterator 對(duì)象,這就意味著這段范圍的元素是通過(guò)讀取所關(guān)聯(lián)的流來(lái)獲得的。這個(gè)構(gòu)造函數(shù)的效果是讀 cin,直到到達(dá)文件結(jié)束或輸入的不是 int 型數(shù)值為止。讀取的元素將用于構(gòu)造 vec 對(duì)象。

ostream_iterator 對(duì)象和 ostream_iterator 對(duì)象的使用

可使用 ostream_iterator 對(duì)象將一個(gè)值序列寫入流中,其操作的過(guò)程與使用迭代器將一組值逐個(gè)賦給容器中的元素相同:

     // write one string per line to the standard output

     ostream_iterator<string> out_iter(cout, "\n");

     // read strings from standard input and the end iterator

     istream_iterator<string> in_iter(cin), eof;

     // read until eof and write what was read to the standard output

     while (in_iter != eof)

         // write value of in_iter to standard output

         // and then increment the iterator to get the next value from cin

        *out_iter++ = *in_iter++;

首先,定義一個(gè) ostream_iterator 對(duì)象,用于將 string 類型的數(shù)據(jù)寫到 cout 中,每個(gè) string 對(duì)象后跟一個(gè)換行符。定義兩個(gè) istream_iterator 對(duì)象,用于從 cin 中讀取 string 對(duì)象。while 循環(huán)類似前一個(gè)例子。但是這一次不是將讀取的數(shù)據(jù)存儲(chǔ)在 vector 對(duì)象中,而是將讀取的數(shù)據(jù)賦給 out_iter,從而輸出到 cout 上。

在類類型上使用 istream_iterator

提供了輸入操作符(>>)的任何類型都可以創(chuàng)建 istream_iterator 對(duì)象。例如,可如下使用 istream_iterator 對(duì)象讀取一系列的 Sales_iter 對(duì)象,并求和:

     istream_iterator<Sales_item> item_iter(cin), eof;

     Sales_item sum; // initially empty Sales_item

     sum = *item_iter++; // read first transaction into sum and get next record

     while (item_iter != eof) {

        if (item_iter->same_isbn(sum))

            sum = sum + *item_iter;

        else {

            cout << sum << endl;

            sum = *item_iter;

        }

        ++item_iter; // read next transaction

     }

     cout << sum << endl; // remember to print last set of records

流迭代器的限制

流迭代器有下面幾個(gè)重要的限制:

不可能從 ostream_iterator 對(duì)象讀入,也不可能寫到 istream_iterator 對(duì)象中。

一旦給 ostream_iterator 對(duì)象賦了一個(gè)值,寫入就提交了。賦值后,沒有辦法再改變這個(gè)值。此外,ostream_iterator 對(duì)象中每個(gè)不同的值都只能正好輸出一次。

ostream_iterator 沒有 -> 操作符。

正如大家所知,算法是基于迭代器操作實(shí)現(xiàn)的。如同前面所述,流迭代器至少定義了一些迭代器操作。由于流迭代器操作,因此,至少可在一些泛型算法上使用這類迭代器??紤]下面的例子,從標(biāo)準(zhǔn)輸入讀取一些數(shù),再將讀取的不重復(fù)的數(shù)寫到標(biāo)準(zhǔn)輸出:

     istream_iterator<int> cin_it(cin);    // reads ints from cin

     istream_iterator<int> end_of_stream;  // end iterator value

     // initialize vec from the standard input:

     vector<int> vec(cin_it, end_of_stream);

     sort(vec.begin(), vec.end());

     // writes ints to cout using " " as the delimiter

     ostream_iterator<int> output(cout, " ");

     // write only the unique elements in vec to the standard output

     unique_copy(vec.begin(), vec.end(), output);

如果程序的輸入是:

     23 109 45 89 6 34 12 90 34 23 56 23 8 89 23

輸出則是:

     6 8 12 23 34 45 56 89 90 109

假設(shè)有一個(gè) vector 容器對(duì)象,存儲(chǔ) 0-9 這 10 個(gè)以升序排列的數(shù)字:

     vector<int> vec;

     for (vector<int>::size_type i = 0; i != 10; ++i)

         vec.push_back(i); // elements are 0,1,2,...9

下面的 for 循環(huán)將以逆序輸出這些元素:

     // reverse iterator of vector from back to front

     vector<int>::reverse_iterator r_iter;

     for (r_iter = vec.rbegin(); // binds r_iter to last element

          r_iter != vec.rend();  // rend refers 1 before 1st element

          ++r_iter)              // decrements iterator one element

         cout << *r_iter << endl;    // prints 9,8,7,...0

雖然顛倒自增和自減這兩個(gè)操作符的意義似乎容易使人迷惑,但是它讓程序員可以透明地向前或向后處理容器。例如,為了以降序排列 vector,只需向 sort 傳遞一對(duì)反向迭代器:

     // sorts vec in "normal" order

     sort(vec.begin(), vec.end());

     // sorts in reverse: puts smallest element at the end of vec

     sort(vec.rbegin(), vec.rend());

反向迭代器需要使用自減操作符

從一個(gè)既支持 -- 也支持 ++ 的迭代器就可以定義反向迭代器,這不用感到吃驚。畢竟,反向迭代器的目的是移動(dòng)迭代器反向遍歷序列。標(biāo)準(zhǔn)容器上的迭代器既支持自增運(yùn)算,也支持自減運(yùn)算。但是,流迭代器卻不然,由于不能反向遍歷流,因此流迭代器不能創(chuàng)建反向迭代器。

如果要輸出列表中最后一個(gè)單詞,可使用反向迭代器:

     // find last element in a comma-separated list

     string::reverse_iterator rcomma = find(line.rbegin(), line.rend(), ',');

因?yàn)榇藭r(shí)傳遞的是 rbegin() 和 rend(),這個(gè)函數(shù)調(diào)用從 line 的最后一個(gè)字符開始往回搜索。當(dāng) find 完成時(shí),如果列表中有逗號(hào),那么 rcomma 指向其最后一個(gè)逗號(hào),即指向反向搜索找到的第一個(gè)逗號(hào)。如果沒有逗號(hào),則 rcomma 的值為 line.rend()。

在嘗試輸出所找到的單詞時(shí),有趣的事情發(fā)生了。直接嘗試:

     // wrong: will generate the word in reverse order

     cout << string(line.rbegin(), rcomma) << endl;

會(huì)產(chǎn)生假的輸出。例如,如果輸入是:

     FIRST,MIDDLE,LAST

then this statement would print TSAL!

則將輸出 TSAL!

圖 11.2 闡明了這個(gè)問(wèn)題:使用反向迭代器時(shí),以逆序從后向前處理 string 對(duì)象。為了得到正確的輸出,必須將反向迭代器 line.rbegin() 和 rcomma 轉(zhuǎn)換為從前向后移動(dòng)的普通迭代器。其實(shí)沒必要轉(zhuǎn)換 line.rbegin(),因?yàn)槲覀冎擂D(zhuǎn)換的結(jié)果必定是 line.end()。只需調(diào)用所有反向迭代器類型都提供的成員函數(shù) base 轉(zhuǎn)換 rcomma 即可

泛型算法 (輸入輸出迭代器和算法綜合介紹) - sentimental_man - sentimental_man的博客

圖 11.2 顯示的對(duì)象直觀地解釋了普通迭代器與反向迭代器之間的關(guān)系。例如,正如 line_rbegin() 和 line.end() 一樣,rcomma 和 rcomma.base() 也指向不同的元素。為了確保正向和反向處理元素的范圍相同,這些區(qū)別必要的。從技術(shù)上來(lái)說(shuō),設(shè)計(jì)普通迭代器與反向迭代器之間的關(guān)系是為了適應(yīng)左閉合范圍(第 9.2.1 節(jié))這個(gè)性質(zhì)的,所以,[line.rbegin(), rcomma) 和 [rcomma.base(), line.end()) 標(biāo)記的是 line 中的相同元素。

反向迭代器用于表示范圍,而所表示的范圍是不對(duì)稱的,這個(gè)事實(shí)可推導(dǎo)出一個(gè)重要的結(jié)論:使用普通的迭代器對(duì)反向迭代器進(jìn)行初始化或賦值時(shí),所得到的迭代器并不是指向原迭代器所指向的元素。

11.3.4. const 迭代器

細(xì)心的讀者可能已經(jīng)注意到,在第 11.1 節(jié)使用 find 的程序中,我們將 result 定義為 const_iterator 類型。這樣做是因?yàn)槲覀儾幌M褂眠@個(gè)迭代器來(lái)修改容器中的元素。

另一方面,雖然第 11.2.1 節(jié)的程序也不打算改變?nèi)萜鲀?nèi)的任何元素,但是它卻使用了普通的非 const 迭代器來(lái)保存 find_first_of 的返回值。這兩種處理存在細(xì)微的差別,值得解釋一下。

原因是,在第二個(gè)例子中,程序?qū)⒌饔米?nbsp;find_first_of 的實(shí)參:

     find_first_of(it, roster1.end(),

                   roster2.begin(), roster2.end());

 

該函數(shù)調(diào)用的輸入范圍由 it 和調(diào)用 roster1.end() 返回的迭代器指定。算法要求用于指定范圍的兩個(gè)迭代器必須具有完全一樣的類型。roster1.end() 返回的迭代器依賴于 roster1 的類型。如果該容器是 const 對(duì)象,則返回的迭代器是 const_iterator 類型;否則,就是普通的 iterator 類型。在這個(gè)程序中,roster1 不是 const 對(duì)象,因而 end 返回的只是一個(gè)普通的迭代器。

如果我們將 it 定義為 const_iterator,那么 find_first_of 的調(diào)用將無(wú)法編譯。用來(lái)指定范圍的兩個(gè)迭代器的類型不相同。it 是 const_iterator 類型的對(duì)象,而 rotser1.end() 返回的則是一個(gè) iterator 對(duì)象。

 

Table 11.3. Iterator Categories

表 11.3. 迭代器種類

Input iterator(輸入迭代器)

讀,不能寫;只支持自增運(yùn)算

Output iterator(輸出迭代器)

寫,不能讀;只支持自增運(yùn)算

Forward iterator(前向迭代器)

讀和寫;只支持自增運(yùn)算

Bidirectional iterator(雙向迭代器)

讀和寫;支持自增和自減運(yùn)算

Random access iterator(隨機(jī)訪問(wèn)迭代器)

讀和寫;支持完整的迭代器算術(shù)運(yùn)算

 

輸入迭代器可用于讀取容器中的元素,但是不保證能支持容器的寫入操作。輸入迭代器必須至少提供下列支持。

相等和不等操作符(==,!=),比較兩個(gè)迭代器。

前置和后置的自增運(yùn)算(++),使迭代器向前遞進(jìn)指向下一個(gè)元素。

用于讀取元素的解引用操作符(*),此操作符只能出現(xiàn)在賦值運(yùn)算的右操作數(shù)上。

箭頭操作符(->),這是 (*it).member 的同義語(yǔ),也就是說(shuō),對(duì)迭代器進(jìn)行解引用來(lái)獲取其所關(guān)聯(lián)的對(duì)象的成員。

輸入迭代器只能順序使用;一旦輸入迭代器自增了,就無(wú)法再用它檢查之前的元素。要求在這個(gè)層次上提供支持的泛型算法包括 find 和 accumulate。標(biāo)準(zhǔn)庫(kù) istream_iterator 類型輸入迭代器。

輸出迭代器 可視為與輸入迭代器功能互補(bǔ)的迭代器;輸出迭代器可用于向容器寫入元素,但是不保證能支持讀取容器內(nèi)容。輸出迭代器要求:

前置和后置的自增運(yùn)算(++),使迭代器向前遞進(jìn)指向下一個(gè)元素。

解引用操作符(*),引操作符只能出現(xiàn)在賦值運(yùn)算的左操作數(shù)上。給解引用的輸出迭代器賦值,將對(duì)該迭代器所指向的元素做寫入操作。

輸出迭代器可以要求每個(gè)迭代器的值必須正好寫入一次。使用輸出迭代器時(shí),對(duì)于指定的迭代器值應(yīng)該使用一次 * 運(yùn)算,而且只能用一次。輸出迭代器一般用作算法的第三個(gè)實(shí)參,標(biāo)記起始寫入的位置。例如,copy 算法使用一個(gè)輸出迭代器作為它的第三個(gè)實(shí)參,將輸入范圍內(nèi)的元素復(fù)制到輸出迭代器指定的目標(biāo)位置。標(biāo)準(zhǔn)庫(kù) ostream_iterator 類型輸出迭代器。

前向迭代器 用于讀寫指定的容器。這類迭代器只會(huì)以一個(gè)方向遍歷序列。前向迭代器支持輸入迭代器和輸出迭代器提供的所有操作,除此之外,還支持對(duì)同一個(gè)元素的多次讀寫。可復(fù)制前向迭代器來(lái)記錄序列中的一個(gè)位置,以便將來(lái)返回此處。需要前向迭代器的泛型算法包括 replace。

雙向迭代器 從兩個(gè)方向讀寫容器。除了提供前向迭代器的全部操作之外,雙向迭代器還提供前置和后置的自減運(yùn)算(--)。需要使用雙向迭代器的泛型算法包括 reverse。所有標(biāo)準(zhǔn)庫(kù)容器提供的迭代器都至少達(dá)到雙向迭代器的要求。

需要隨機(jī)訪問(wèn)迭代器的泛型算法包括 sort 算法。vector、deque 和 string 迭代器是隨機(jī)訪問(wèn)迭代器,用作訪問(wèn)內(nèi)置數(shù)組元素的指針也是隨機(jī)訪問(wèn)迭代器。

隨機(jī)訪問(wèn)迭代器 提供在常量時(shí)間內(nèi)訪問(wèn)容器任意位置的功能。這種迭代器除了支持雙向迭代器的所有功能之外,還支持下面的操作:

The relational operators <, <=, >, and >= to compare the relative positions of two iterators.

關(guān)系操作符 <、<=、> 和 >=,比較兩個(gè)迭代器的相對(duì)位置。

迭代器與整型數(shù)值 n 之間的加法和減法操作符 +、+=、- 和 -=,結(jié)果是迭代器在容器中向前(或退回)n 個(gè)元素。

兩個(gè)迭代器之間的減法操作符(--),得到兩個(gè)迭代器間的距離。

下標(biāo)操作符 iter[n],這是 *(iter + n) 的同義詞。

盡管 map 和 set 類型提供雙向迭代器,但關(guān)聯(lián)容器只能使用算法的一個(gè)子集。問(wèn)題在于:關(guān)聯(lián)容器的鍵是 const 對(duì)象。因此,關(guān)聯(lián)容器不能使用任何寫序列元素的算法。只能使用與關(guān)聯(lián)容器綁在一起的迭代器來(lái)提供用于讀操作的實(shí)參。

向算法傳遞無(wú)效的迭代器類別所引起的錯(cuò)誤,無(wú)法保證會(huì)在編譯時(shí)被捕獲到。

11.4.1. 算法的形參模式

任何其他的算法分類都含有一組形參規(guī)范。理解這些形參規(guī)范有利于學(xué)習(xí)新的算法——只要知道形參的含義,就可專注于了解算法實(shí)現(xiàn)的操作。大多數(shù)算法采用下面四種形式之一:

     alg (beg, end, other parms);

     alg (beg, end, dest, other parms);

     alg (beg, end, beg2, other parms);

     alg (beg, end, beg2, end2, other parms);

其中,alg 是算法的名字,beg 和 end 指定算法操作的元素范圍。我們通常將該范圍稱為算法的“輸入范圍”。盡管幾乎所有算法都有輸入范圍,但算法是否使用其他形參取決于它所執(zhí)行的操作。這里列出了比較常用的其他形參:dest、beg2 和 end2,它們都是迭代器。這些迭代器在使用時(shí),充當(dāng)類似的角色。除了這些迭代器形參之外,有些算法還帶有其他的菲迭代器形參,它們是這些算法特有的。

新對(duì)容器元素排序的算法要使用 < 操作符。這些算法的第二個(gè)重載版本帶有一個(gè)額外的形參,表示用于元素排序的不同運(yùn)算:

     sort (beg, end);         // use < operator to sort the elements

     sort (beg, end, comp);   // use function named comp to sort the elements

檢查指定值的算法默認(rèn)使用 == 操作符。系統(tǒng)為這類算法提供另外命名的(而非重載的)版本,帶有謂詞函數(shù)(第 11.2.3 節(jié))形參。帶有謂詞函數(shù)形參的算法,其名字帶有后綴 _if:

檢查指定值的算法默認(rèn)使用 == 操作符。系統(tǒng)為這類算法提供另外命名的(而非重載的)版本,帶有謂詞函數(shù)(第 11.2.3 節(jié))形參。帶有謂詞函數(shù)形參的算法,其名字帶有后綴 _if:

     find(beg, end, val);       // find first instance of val in the input range

     find_if(beg, end, pred);   // find first instance for which pred is true

上述兩個(gè)算法都在輸入范圍內(nèi)尋找指定元素的第一個(gè)實(shí)例。其中,find 算法查找一個(gè)指定的值,而 find_if 算法則用于查找一個(gè)使謂詞函數(shù) pred 返回非零值的元素。

區(qū)別是否實(shí)現(xiàn)復(fù)制的算法版本

無(wú)論算法是否檢查它的元素值,都可能重新排列輸入范圍內(nèi)的元素。在默認(rèn)情況下,這些算法將重新排列的元素寫回其輸入范圍。標(biāo)準(zhǔn)庫(kù)也為這些算法提供另外命名的版本,將元素寫到指定的輸出目標(biāo)。此版本的算法在名字中添加了 _copy 后綴:

     reverse(beg, end);

     reverse_copy(beg, end, dest);

reverse 函數(shù)的功能就如它的名字所意味的:將輸入序列中的元素反射重新排列。其中,第一個(gè)函數(shù)版本將自己的輸入序列中的元素反向重排。而第二個(gè)版本,reverse_copy,則復(fù)制輸入序列的元素,并將它們逆序存儲(chǔ)到 dest 開始的序列中。

list 容器上的迭代器是雙向的,而不是隨機(jī)訪問(wèn)類型。由于 list 容器不支持隨機(jī)訪問(wèn),因此,在此容器上不能使用需要隨機(jī)訪問(wèn)迭代器的算法。這些算法包括 sort 及其相關(guān)的算法。還有一些其他的泛型算法,如 merge、remove、reverse 和 unique,雖然可以用在 list 上,但卻付出了性能上的代價(jià)。如果這些算法利用 list 容器實(shí)現(xiàn)的特點(diǎn),則可以更高效地執(zhí)行。

如果可以結(jié)合利用 list 容器的內(nèi)部結(jié)構(gòu),則可能編寫出更快的算法。與其他順序容器所支持的操作相比,標(biāo)準(zhǔn)庫(kù)為 list 容器定義了更精細(xì)的操作集合,使它不必只依賴于泛型操作。表 11.4 列出了 list 容器特有的操作,其中不包括要求支持雙向或更弱的迭代器類型的泛型算法,這類泛型算法無(wú)論是用在 list 容器上,還是用在其他容器上,都具有相同的效果。

Table 11.4. list-Specific Operations

表 11.4. list 容器特有的操作

 lst.merge(lst2) lst.merge(lst2, comp)

將 lst2 的元素合并到 lst 中。這兩個(gè) list 容器對(duì)象都必須排序。lst2 中的元素將被刪除。合并后,lst2 為空。返回 void 類型。第一個(gè)版本使用 < 操作符,而第二個(gè)版本則使用 comp 指定的比較運(yùn)算

 lst.remove(val) lst.remove_if(unaryPred)

調(diào)用 lst.erase 刪除所有等于指定值或使指定的謂詞函數(shù)返回非零值的元素。返回 void 類型

lst.reverse()

反向排列 lst 中的元素

lst.sort

對(duì) lst 中的元素排序

 lst.splice(iter, lst2)

lst.splice(iter, lst2, iter2)
lst.splice(iter, beg, end)

將 lst2 的元素移到 lst 中迭代器 iter 指向的元素前面。在 lst2 中刪除移出的元素。第一個(gè)版本將 lst2 的所有元素移到 lst 中;合并后,lst2 為空。lst 和 lst2 不能是同一個(gè) list 對(duì)象。第二個(gè)版本只移動(dòng) iter2 所指向的元素,這個(gè)元素必須是 lst2 中的元素。在這種情況中,lst 和 lst2 可以是同一個(gè) list 對(duì)象。也就是說(shuō),可在一個(gè) list 對(duì)象中使用 splice 運(yùn)算移動(dòng)一個(gè)元素。第三個(gè)版本移動(dòng)迭代器 beg 和 end 標(biāo)記的范圍內(nèi)的元素。beg 和 end 照例必須指定一個(gè)有效的范圍。這兩個(gè)迭代器可標(biāo)記任意 list 對(duì)象內(nèi)的范圍,包括 lst。當(dāng)它們指定 lst 的一段范圍時(shí),如果 iter 也指向這個(gè)范圍的一個(gè)元素,則該運(yùn)算未定義。

 lst.unique() lst.unique(binaryPred)

調(diào)用 erase 刪除同一個(gè)值的團(tuán)結(jié)副本。第一個(gè)版本使用 == 操作符判斷元素是否相等;第二個(gè)版本則使用指定的謂詞函數(shù)實(shí)現(xiàn)判斷

 

大多數(shù) list 容器特有的算法類似于其泛型形式中已經(jīng)見過(guò)的相應(yīng)的算法,但并不相同:

     l.remove(val);     // removes all instances of val from 1

     l.remove_if(pred); // removes all instances for which pred is true from 1

     l.reverse();       // reverses the order of elements in 1

     l.sort();          // use element type < operator to compare elements

     l.sort(comp);      // use comp to compare elements

     l.unique();        // uses element == to remove adjacent duplicates

     l.unique(comp);    // uses comp to remove duplicate adjacent copies

list 容器特有的算法與其泛型算法版本之間有兩個(gè)至關(guān)重要的差別。其中一個(gè)差別是 remove 和 unique 的 list 版本修改了其關(guān)聯(lián)的基礎(chǔ)容器:真正刪除了指定的元素。例如,list::unique 將 list 中第二個(gè)和后續(xù)重復(fù)的元素刪除出該容器。

 

與對(duì)應(yīng)的泛型算法不同,list 容器特有的操作能添加和刪除元素。

另一個(gè)差別是 list 容器提供的 merge 和 splice 運(yùn)算會(huì)破壞它們的實(shí)參。使用 merge 的泛型算法版本時(shí),合并的序列將寫入目標(biāo)迭代器指向的對(duì)象,而它的兩個(gè)輸入序列保持不變。但是,使用 list 容器的 merge 成員函數(shù)時(shí),則會(huì)破壞它的實(shí)參 list 對(duì)象——當(dāng)實(shí)參對(duì)象的元素合并到調(diào)用 merge 函數(shù)的 list 對(duì)象時(shí),實(shí)參對(duì)象的元素被移出并刪除。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多