泛型算法 - 2【C++ Primer 學習筆記 - 第十一章】

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


vector<int> ivec;
// istream_iterator 可以與特定流綁定,
// 也可以不提供實參,指向超出末端位置
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;


// #include <fstream>
ofstream outfile;
// ostream_iterator 必須綁定特定流
// 第二個參數可選,指定寫入輸出流時的分隔符(必須是C風格字符串)
ostream_iterator<int> output(outfile, " ");

// 讀取 cin,直到文件結束,或者輸入的不是 int 類型爲止
while(in_iter != eof)
	ivec.push_back(*in_iter++);

// 等效於上面的循環
vector<int> ivec2(in_iter, eof);



ostream_iterator<string> out_iter(cout, "\n");
istream_iterator<string> in_iter(cin);
istream_iterator<string> eof;
while(in_iter != eof)
	*out_iter++ = *in_iter++;


// P352:類類型上使用 istream_iterator

istream_iterator<int> cin_it(cin);
istream_iterator<int> end_of_stream;
vector<int> ivec(cin_it, end_of_stream);
sort(ivec.begin(), ivec.end());
ostream_iterator<int> output(cout, " ");
unique_copy(ivec.begin(), ivec.end(), output);


反向迭代器

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

// 反向輸出
vector<int>::reverse_iterator r_iter;
for(r_iter=ivec.rbegin(); r_iter!=ivec.rend(); ++r_iter)
	cout << *r_iter << endl;

// 正常的升序排列
sort(ivec.begin(), ivec.end());

// 利用反向迭代器,實現降序排列
sort(ivec.rbegin(), ivec.rend());

顯然,反向迭代器,必須支持 ++ 和 -- 的操作。
因爲,它的目的就是移動迭代器進行反向遍歷。
流迭代器,由於無法反向遍歷流,因此流迭代器不能創建反向迭代器。


string line("zhangsan,lisi,wanggang");
string::iterator comma = find(line.begin(), line.end(), ',');
cout << string(line.begin(), comma) << endl;

// find 的第三個參數,注意:單引號,而非雙引號
string::reverse_iterator rcomma = find(line.rbegin(), line.rend(), ',');

// line.end() 指向超出末端元素
// line.rbegin() 指向最後一個元素
// 同理:comma 指向的元素, 和 rcomma指向的元素是不同的

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

// wanggang
cout << string(rcomma.base(), line.end()) << endl;


const 迭代器

不希望使用該迭代器來修改容器中的元素,則可以使用 const 迭代器



五種迭代器

輸入迭代器:讀,不能寫;只支持自增運算
輸出迭代器:寫,不能讀;只支持自增運算
前向迭代器:讀、寫;只支持自增運算
雙向迭代器:讀、寫;支持自增、自減運算
隨機訪問迭代器:讀、寫;支持完整的迭代器算術運算

1、輸入迭代器,可用於讀取容器中的元素,但是,不保證能支持容器的寫入操作。(如:istream_iterator)
2、輸出迭代器,可用於向容器寫入元素,但是,不保證能支持讀取容器內容。(如:ostream_iterator)
      對於指定的迭代器值,只能使用一次解引用操作
3、前向迭代器,只會以一個方向遍歷序列,支持對同一個元素的多次讀寫。(replace 算法,需要前向迭代器)
4、雙向迭代器,提供前向迭代器的所有操作,另外,還提供前置、後置的自減運算(--)。
      標準庫容器提供的迭代器,至少達到雙向迭代器的要求。(reverse 算法,需要雙向迭代器)
5、隨機訪問迭代器。vector、deque、 string 等,它們的迭代器是隨機訪問迭代器,
      內置數組的指針也是隨機訪問迭代器


map、set、list 類型提供的是雙向迭代器,
string、vector、deque 容器,提供的是隨機訪問迭代器
內置數組的指針,也是隨機訪問迭代器
istream_iterator 是,輸入迭代器
ostream_iterator 是,輸出迭代器

事實上, 雖然 map 、set 提供的是雙向迭代器,但卻只能使用算法的子集。
因爲,關聯容器的鍵是 const 對象,無法使用任何寫序列元素的算法。


算法最基本的性質,是需要使用的迭代器類型。

算法的形參模式
多數算法,都採用以下4 種形式中的一種:
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 指定算法操作的元素範圍(輸入範圍);
dest、beg2、end2, 都是迭代器


帶有單個目標迭代器的算法
dest 形參是一個迭代器,用於指定,存儲輸出數據的目標對象。
爲保證寫入安全,通常使用插入迭代器 或 ostream_iterator 來調用算法,
否則,算法將假定容器內有足夠多個需要的元素


帶第二個輸入序列的算法
有些算法,帶有一個beg2,或者帶有beg2 和 end2,用於指定第二個輸入範圍。

算法的命名規範

標準庫使用一組相同的命名重載規範
它們包括2 種模式:
1、檢測輸入範圍內元素的算法
2、應用於對輸入範圍內元素重新排序的算法


重新排序的算法,要使用 < 操作符。此類算法的第二個重載版本帶有額外形參,表示用於排序的不同運算
sort (beg, end);
sort (beg, end, comp);

檢查指定值的算法,默認使用 == 操作符。此類算法,會提供另外命名的版本,而不是重載版本。
因爲,這兩種版本的算法,帶有相同數目的形參。如果使用重載,容易混淆。
find ( beg, end, val);
find_if (beg, end, pred);


區別是否實現複製的算法版本
算法的執行,很可能會重新排列輸入範圍內的元素,
而排列之後的元素會被寫回輸入範圍。
標準庫提供了另外命名的版本,將元素寫到指定的輸出目標。
reverse( beg, end);
reverse_copy (beg, end, dest);


list 容器上的迭代器是雙向的,而不是隨機訪問類型。
因此,一些泛型算法,用在 list 容器上,會付出更多的性能上的代價。
於是,標準庫爲 list 容器定義了更精細的操作集合,使其不必只依賴於泛型操作。


list 容器特有的操作
lst.merge (lst2)
lst.merge (lst2, comp)
將 lst2 的元素合併到 lst 中。這兩個 list 容器對象都必須排序。
lst2 中的元素將被刪除。合併後,lst2 爲空。返回 void 
第一個版本,使用 < 操作符
第二個版本,使用 comp 指定的比較運算


lst.remove (val)
lst.remove_if (unaryPred)
調用 lst.erase 刪除所有等於指定值 或 使指定的謂詞函數返回非零值的元素。返回void

lst.reverse ()
反向排列 lst 中的元素

lst.sort ()
對 lst 中的元素排序

lst.splice (iter, lst2)
lst.splice (iter, lst2, iter2)
lst.splice (iter, beg, end)
將 lst2 的元素移動到 lst 中迭代器 iter 所指向的元素前面。在 lst2 中刪除移出的元素。
第一個版本將 lst2 的所有元素移動到 lst 中,合併後, lst2 爲空。
lst1 和 lst2 不能是同一個對象

第二個版本只移動 iter2 所指向的元素,這個元素必須是 lst2 中的元素。
lst1 和 lst2 可以是同一個對象。即:可以在 list 對象中使用 splice 運算移動一個元素

第三個版本,移動迭代器 beg 和 end 標記的範圍內的元素。該範圍必須有效。
beg 和 end 可以標記任意的 list 對象內的範圍,包括 lst 自身。
但是,如果,iter 指向的元素,在標記範圍之中,則,運算未定義

lst.unique ()
lst.unique (binaryPred)

調用 erase 刪除同一個值的連續副本。
第一個版本使用 == 操作符,判斷元素是否相等。
第二個版本使用指定的謂詞函數實現判斷

對於 list 對象,應該優先使用 list 容器特有的成員版本,而不是泛型算法

list 容器特有的算法,與泛型算法之間,有兩個至關重要的差別。
1、 remove 和 unique 的 list 版本,修改了其關聯的基礎容器,即:真正刪除了指定的元素。
2、 merge 和 splice 的 list 版本,會破壞它們的實參。
使用 merge 的泛型算法時,合併的序列將寫入目標迭代器指向的對象,
而它的兩個輸入序列保持不變。
而,list 容器的 merge 成員函數,會破壞它的實參 list 對象:
實參對象的元素合併到調用 merge 函數的 list 對象時,實參對象的元素將被移出並刪除。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章