10.1概述
大多數定義在頭文件裏,部分數值泛型算法定義在裏。
算法不依賴於容器類型,但依賴於元素類型操作(即關係運算)。
對於內置數組,可以使用 begin(數組名)、end(數組名)來獲取對應指針,使算法對內置數組進行類似容器的處理。
算法並不真正執行容器本身操作,一切操作都基於迭代器。
10.2簡單泛型算法的使用
只讀算法
count
- 接收:一對迭代器和一個值。
- 返回:該值出現的次數。
find
- 接收:一對迭代器和一個值。
- 返回:迭代器。
accumulate
- 接收:一對迭代器和一個初值。
- 返回:累加求和結果。
- 誤區:string可以實現,但const char* 不可以實現。
equal
- 接收:第一個序列的一對迭代器和第二個序列的首迭代器。
- 返回:true/false。
- 誤區:string可以實現,但const char* 不可以實現。
寫算法
fill
- 接收:一對迭代器和一個用於賦值的值。
fill_n
- 接收:一個迭代器表示開始位置,一個值表示賦值個數,一個值用於賦值。
- 誤區:不能給空容器賦值。
back_inserter
- 頭文件:iterator
- 功能:向容器末尾插入元素(結合fill_n可解決空容器問題)。
- 例子
vector<int>vec; auto it=back_inserter(vec); fill_n(it,10,0);
- 重排算法
- unique
- 接收:一對迭代器。
- 返回:指向重排後不重複區域後一個位置的迭代器。
- 和erase方法配套可以去重。
- unique
10.3 自定義操作
謂詞:一個可調用的表達式,返回結果爲一個能用作條件的值,分一元謂詞與二元謂詞(也就是自定義取代默認操作符)。
可調用對象
- 定義:可以對其使用調用運算符(())的對象或表達式。
- 包括:函數與函數指針、重載函數調用運算符的類、lambda表達式。
lambda表達式
- 定義:可調用的代碼單元,類似於未命名的內聯函數,格式爲:
[capture list](parameter list)−>return type {function body} - 特點:
- 可定義在函數內部。
- 必須使用尾置返回,但返回類型與參數列表可忽略,同時自動推斷返回類型(若包含單一return之外的語句則返回void)。
- 不能有默認參數。
例子
auto f = [](const string &a, const string &b) {return a.size() < b.size();}; stable_sort(s.begin(), s.end(), f());
或者直接
stable_sort(s.begin(), s.end(), [](const string &a, const string &b) {return a.size() < b.size();});
捕獲方式:
- 顯示捕獲
- 值捕獲
- 如果想在lambda表達式中改變該值,須在參數列表後,表達式體前加關鍵字 mutable。
- 引用捕獲
- 值捕獲
- 隱式捕獲:只寫明捕獲方式而不寫明捕獲的具體對象。
- 顯示捕獲
- 定義:可調用的代碼單元,類似於未命名的內聯函數,格式爲:
函數替代lambda表達式
思路:運用bind函數
bind函數
- 頭文件:functional
- 一般形式:
auto newCallable=bind(callable, arg_list)
其中callable爲相關函數,arg_list爲逗號分隔的 參數列表。
- arg_list
- _n:佔位符,表示綁定函數的第n個參數。
- 通常爲_n與實參混合使用。
- 其中_n必須顯示聲明using namespace std::placeholders。
- 一個巧妙運用
sort(words.begin(), words.end(), bind(isShorter, _2, _1));
- 運用ref()函數綁定參數實現引用傳遞。
10.4 頭文件中的迭代器
插入迭代器:back_inserter、front_inserter、inserter。
IO迭代器:
istream_iterator
- 如果綁定流則從流的第一個位置開始,不綁定則爲尾後位置。
輔助創建容器
istream_iterator<int>in(cin),eof; vector<int>v(in,eof);
迭代器被解引用時才真正從流中讀取數據。
ostream_iterator
- 通過綁定的可以爲每次輸出附加一個固定輸出。
- 輔助容器輸出(推薦未註釋的方法)
for(auto e : v) out = e; // for(auto e : v) // *out++ = e; // copy(v.begin(), v.end(), out);
- 反向迭代器
會出現倒序輸出的情況(有時來說是個問題),可以使用base()方法使反向迭代器轉化爲普通迭代器。
10.5 泛型算法結構
五類迭代器(層次由低到高)
輸入迭代器
- 支持 ==、!=
- 支持前置、後置自增
- 支持解引用*與->運算符
- 只能用於單遍掃描,可支持算法find、accumulate,istream_iterator是輸入迭代器。
輸出迭代器
- 輸入迭代器的補集
- 支持前置、後置自增
- 支持解引用運算符
- 出現在賦值運算符的左側
- 支持copy的第三個參數,ostream_iterator是輸出迭代器
- 前向迭代器
- 輸入輸出迭代器的並集
- 支持replace算法,forward_list的迭代器是前向迭代器
- 雙向迭代器
- 支持前置、後置自減運算符
- 支持reverse算法
- 隨機訪問迭代器
- 比較運算符
- 單迭代器的加減運算符
- 兩個迭代器的減運算符
- 下標運算符
- 支持sort,array,deque,string,vector都是隨機訪問迭代器,內置數組的指針也是。
接收單個目標迭代器的算法,一般可以用輸入迭代器代替寫入目的位置的迭代器以保證安全。
算法命名規範
- 一些算法使用重載,接收謂詞來代替==或<運算符。
- _if版本的算法,通過謂詞來代替元素值。
- 一些算法增加_copy版本把新的結果存儲到其他容器中
鏈表的特定算法
- merge:分治合併(有重載的謂詞版本)
- remove:刪除元素(有remove_if的謂詞版本)
- reverse、sort、unique
- splice:將一個鏈表的一部分移動到另一個鏈表中,其中若調用者爲雙向鏈表,則移動到位置之前,否則爲位置之後。
- 鏈表特有的算法會改變底層容器。