天天看點

C++程式設計:原理與實踐(進階篇)16.7 拷貝

<b>16.7 拷貝</b>

<b></b>

在16.2節中,我們認為f?ind()是“最簡單的有用算法”。當然,這一點可以讨論。很多簡單算法都是有用的——甚至其中有些編寫起來有些過于簡單了。當你可以使用其他人編寫和調試好的代碼時,為什麼要費力編寫新的代碼?當談及簡單性和有效性時,copy()可以與f?ind()媲美。stl提供了三個版本的拷貝:

拷貝操作

copy(b,e,b2) 将[b:e)拷貝到[b2:b2+(e-b))

unique_copy(b,e,b2) 将[b:e)拷貝到[b2:b2+(e-b)),禁止拷貝相鄰的相同元素

copy_if(b,e,b2,p) 将[b:e)拷貝到[b2:b2+(e-b)),但是僅拷貝滿足謂詞p的元素

16.7.1 基本拷貝算法

基本拷貝算法的定義如下:

給定一對疊代器,copy()将一個序列拷貝到另一個序列,目的序列用一個疊代器指明首元素。例如:

注意,copy()的輸入序列類型可以與輸出序列類型不同。這是stl算法的一種有用泛化:它們可用于各種序列,而無須對其實作做不必要的假設。我們要記得檢查在輸出序列中是否有足夠的空間以儲存拷貝來的元素。檢查空間的大小是程式員的責任。stl算法的設計目标是最大的通用性和最佳的性能,它們(預設)沒有做範圍檢查和其他代價昂貴的測試來保護使用者。有時候,你可能希望它們做這些檢查,但是當你想進行檢查時,你可以像上面代碼那樣自己來完成。

16.7.2 流疊代器

你可能聽到過短語“拷貝到輸出”和“從輸入拷貝”。這種思考方式對某種形式的i/o來說是很好、很有用的,我們确實可以用copy()做這些事情。

記住,一個序列是這樣的東西:

它有開始和結尾;

我們可以用++移動到下一個元素;

我們可以用*得到目前元素的值。

我們可以很容易地用這種方式表示輸入和輸出流。例如:

你可以想象如何來實作它。标準庫提供了一個ostream_iterator類型,就可以這樣工作;ostream_iterator&lt;t&gt;是一個疊代器,你可以用它寫入類型為t的值。

類似地,标準庫提供了istream_iterator&lt;t&gt;類型用于讀取類型為t的值:

通過ostream_iterator和istream_iterator,我們可以對自己的i/o使用copy()。例如,我們可以實作一個“快速和混亂的”字典,如下所示:

疊代器eos是表示“輸入結束”的流疊代器。當一個istream到達輸入結束(經常被表示為eof),它的istream_iterator将等于預設的istream_iterator(這裡稱為eos)。

注意,我們使用一對疊代器來初始化vector。作為一個容器的初始化器,一對疊代器(a, b)表示“将序列[a:b)讀取到容器”。自然地,我們使用的一對疊代器是(ii,eos)——輸入的開始與結束。這令我們不必使用&gt;&gt;和push_back()。我們強烈建議不要使用下面的替代方案。

那些試圖猜測輸入的最大規模的人,通常會發現他們低估了輸入規模,進而遇到嚴重的問題——緩沖區溢出,無論對于他們自己還是他們的使用者都是很嚴重的問題。這種溢出也是安全問題的一個來源。

試一試

首先,編譯上面的程式令其正确運作,用一個小檔案來測試它,例如一個包含幾百個單詞的檔案。然後,嘗試我們着重強調不推薦的猜測輸入規模的版本,觀察當輸入緩沖區b溢出時發生什麼。注意,最壞的情況是在特定例子中溢出沒有導緻任何錯誤,這樣你就可能試圖将它傳遞給使用者。

在這個小程式中,我們讀取單詞然後進行排序。這在當時看來是一個明顯的解決方案,但是我們為什麼要将單詞放在“錯誤的位置”,以至于随後我們不得不進行排序?更糟糕的是,我們發現一個單詞在輸入中出現幾次,我們就會儲存和列印它幾次。

我們可以用unique_copy()代替copy()來解決後一個問題。unique_copy()不會重複拷貝相同的值。例如,如果使用普通的copy(),輸入

the man bit the dog

程式會生成

如果我們使用unique_copy(),程式将會輸出

這些換行是從哪裡來的?帶有分隔符的輸出是很常見的,ostream_iterator的構造函數允許你(可選的)指定在每個值之後列印一個字元串:

很明顯,對于供人類閱讀的輸出來說,換行分隔符是很常見的選擇,但是也許我們喜歡使用空格作為分隔符呢?可以編寫代碼如下:

這将會生成輸出

16.7.3 使用set保持順序

有一個更容易的方式來得到上面那樣的輸出,使用set而不是vector:

當我們将值插入一個set時,重複的值被忽略掉。而且,set中的元素是按順序儲存的,是以不需要進行排序。通過使用正确的工具,大多數任務很容易完成。

16.7.4 copy_if

copy()算法進行無條件拷貝。unique_copy()算法禁止拷貝相同的相鄰元素。第三種拷貝算法隻拷貝令謂詞為真的元素:

使用16.4節中的larger_than函數對象,我們可以找到一個序列中大于6的所有元素,如下所示:

由于我犯的一個錯誤,這個算法錯失進入1998 iso标準的機會。這個錯誤現在已經被補救了,但是你仍然可以找到沒有copy_if的c++實作。如果是這樣,請使用本節中的定義。

繼續閱讀