-
迭代器令算法不依賴于容器,,但算法依賴于元素類型的操作,。
-
算法永遠(yuǎn)不會(huì)執(zhí)行容器的操作。算法永遠(yuǎn)不會(huì)改變底層容器的大小,。
-
accumulate定義在頭文件numeric中,,接受三個(gè)參數(shù),前兩個(gè)指出需要求和的元素的范圍,,第三個(gè)參數(shù)是和的初值,。accumulate的第三個(gè)參數(shù)的類型決定了函數(shù)中使用哪個(gè)加法運(yùn)算符以及返回值的類型。
// 對(duì)vec中的元素求和,,和的初值是0
int sum = accumulate(vec.cbegin(), vec.cend(), 0);
// 將vector中所有string元素連接起來(lái)
string sum = accumulate(v.cbegin(), v.cend(), string(""));
// 錯(cuò)誤:const char*上沒(méi)有定義+運(yùn)算符
string sum = accumulate(v.cbegin(), v.cend(), "");
-
對(duì)于只讀取而不改變?cè)氐乃惴?,通常最好使用cbegin()和cend()。但是,,如果你計(jì)劃使用算法返回的迭代器來(lái)改變?cè)氐闹?,就需要使用begin()和end()的結(jié)果作為參數(shù)。
-
equal用于確定兩個(gè)序列是否保存相同的值,。它將第一個(gè)序列中的每個(gè)元素與第二個(gè)序列中的對(duì)應(yīng)元素進(jìn)行比較,。如果所有對(duì)應(yīng)元素都相等,則返回true,,否則返回false,。此算法接受三個(gè)迭代器:前兩個(gè)表示第一個(gè)序列中的元素范圍,第三個(gè)表示第二個(gè)序列的首元素,。
-
那些只接受一個(gè)單一迭代器來(lái)表示第二個(gè)序列的算法,,都假定第二個(gè)序列至少與第一個(gè)序列一樣長(zhǎng)。
-
算法fill接受一對(duì)迭代器表示一個(gè)范圍,,還接受一個(gè)值作為第三個(gè)參數(shù),。fill將給定的這個(gè)值賦予輸入序列中的每個(gè)元素,。
-
一些算法從兩個(gè)序列中讀取元素,。構(gòu)成這兩個(gè)序列的元素可以來(lái)自于不同類型的容器。而且,,兩個(gè)序列中元素的類型也不要求嚴(yán)格匹配,。算法要求的只是能夠比較兩個(gè)序列中的元素。
-
函數(shù)fill_n接受一個(gè)單迭代器、一個(gè)計(jì)數(shù)值和一個(gè)值,。它將給定值賦予迭代器指向的元素開(kāi)始的指定個(gè)元素,。示例:fill_n(dest, n, val) 。fill_n假定dest指向一個(gè)元素,,而從dest開(kāi)始的序列至少包含n個(gè)元素,。
-
向目的位置迭代器寫入數(shù)據(jù)的算法假定目的位置足夠大,能容納要寫入的元素,。
-
插入迭代器是一種像容器中添加元素的迭代器,。通常情況,當(dāng)我們通過(guò)一個(gè)迭代器向容器元素賦值時(shí),,值被賦予迭代器指向的元素,。而當(dāng)我們通過(guò)一個(gè)插入迭代器賦值時(shí),一個(gè)與賦值號(hào)右側(cè)值相等的元素被添加到容器中,。
-
back_inserter定義在頭文件iterator中,,接受一個(gè)指向容器的引用,返回一個(gè)與該容器綁定的插入迭代器,。當(dāng)我們通過(guò)此迭代器賦值時(shí),,賦值運(yùn)算符會(huì)調(diào)用push_back將一個(gè)具有給定值的元素添加到容器中:
vector<int> vec; // 空向量
auto it = back_inserter(vec); // 通過(guò)它賦值會(huì)將元素添加到vec中
*it = 42; // vec中現(xiàn)在有一個(gè)元素,值為42
// 我們常常使用back_inserter來(lái)創(chuàng)建一個(gè)迭代器,,作為算法的目的位置來(lái)使用
vector<int> vec; // 空向量
// 正確:back_inserter創(chuàng)建一個(gè)插入迭代器,,可用來(lái)向vec添加元素
fill_n(back_inserter(vec), 10, 0); // 添加10個(gè)元素到vec
-
拷貝算法copy接受三個(gè)迭代器,前兩個(gè)表示一個(gè)輸入范圍,,第三個(gè)表示目的序列的起始位置,。此算法將輸入范圍中的元素拷貝到目的序列中。傳遞給copy的目的序列至少要包含與輸入序列一樣多的元素,。
// 利用copy實(shí)現(xiàn)內(nèi)置數(shù)組的拷貝
int a1[] = {0,1,2,3,4,5,6,7,8,9};
int a2[sizeof(a1)/sizeof(*a1)]; // a2與a1大小一樣
// ret指向拷貝到a2的尾元素之后的位置
auto ret = copy(begin(a1), end(a1), a2); // 把a(bǔ)1的內(nèi)容拷貝給a2
-
copy返回的是其目的位置迭代器(遞增后)的值,。即,ret恰好指向拷貝到a2的尾元素之后的位置,。
-
replace算法讀入一個(gè)序列,,并將其中所有等于給定值的元素都改為另一個(gè)值。此算法接受4個(gè)參數(shù):前兩個(gè)是迭代器,,表示輸入序列,,后兩個(gè)一個(gè)是要搜索的值,另一個(gè)是新值,。它將所有等于第一個(gè)值的元素替換為第二個(gè)值:
// 將所有值為0的元素改為42
replace(ilst.begin(), ilst.end(), 0, 42);
-
如果我們希望保留原序列不變,,可以調(diào)用replace_copy。此算法接受額外第三個(gè)迭代器參數(shù),,指出調(diào)整后序列的保存位置:
// 使用back_inserter按需要增長(zhǎng)目的序列
replace_copy(ilst.cbegin(), ilst.cend(), back_inserter(ivec), 0, 42);
-
sort算法接受兩個(gè)迭代器,,表示要排序的元素范圍,。
-
unique算法重排輸入序列,將相鄰的重復(fù)項(xiàng)“消除”,,并返回一個(gè)指向不重復(fù)值范圍的末尾的迭代器,。unique返回的迭代器指向最后一個(gè)不重復(fù)元素之后的位置,此位置之后的元素仍然存在,,但我們不知道它們的值是什么,。
-
標(biāo)準(zhǔn)庫(kù)算法對(duì)迭代器而不是容器進(jìn)行操作。因此,,算法不能(直接)添加或刪除元素,。
void elimDups(vector<string> &words)
{
// 按字典序排序words,以便查找重復(fù)單詞
sort(words.begin(), words.end());
// unique重排輸入范圍,,使得每個(gè)單詞只出現(xiàn)一次
// 排列在范圍的前部,,返回指向不重復(fù)區(qū)域之后一個(gè)位置的迭代器
auto end_unique = unique(words.begin(), words.end());
// 使用向量操作erase刪除重復(fù)單詞
words.erase(end_unique, words.end());
}
-
謂詞是一個(gè)可調(diào)用的表達(dá)式,其返回結(jié)果是一個(gè)能用作條件的值,。標(biāo)準(zhǔn)庫(kù)算法所使用的謂詞分為兩類:一元謂詞(意味著它們只接受單一參數(shù))和二元謂詞(意味著它們有兩個(gè)參數(shù)),。接受謂詞參數(shù)的算法對(duì)輸入序列中的元素調(diào)用謂詞。因此,,元素類型必須能轉(zhuǎn)換為謂詞的參數(shù)類型,。
// 比較函數(shù),用來(lái)按長(zhǎng)度排序單詞
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
-
穩(wěn)定排序算法stable_sort維持相等元素的原有順序,。假定在此調(diào)用前words是按字典序排列的,,則調(diào)用之后,words會(huì)按元素大小排序,,而長(zhǎng)度相同的單詞會(huì)保持字典序:
elimDups(words); // 將words按字典序重排,,并消除重復(fù)單詞
// 按長(zhǎng)度重新排序,長(zhǎng)度相同的單詞維持字典序
stable_sort(words.begin(), words.end(), isShorter);
for(const auto &s : words) // 無(wú)需拷貝字符串
cout << s << " "; // 打印每個(gè)元素,,以空格分隔
cout << endl;
-
標(biāo)準(zhǔn)庫(kù)定義了名為partition的算法,,它接受一個(gè)謂詞,對(duì)容器內(nèi)容進(jìn)行劃分,,使得謂詞為true的值會(huì)排在容器的前半部分,,而使謂詞為false的值會(huì)排在后半部分。算法返回一個(gè)迭代器,,指向最后一個(gè)使謂詞為true的元素之后的位置,。
-
find_if算法接受一對(duì)迭代器,表示一個(gè)范圍,,第三個(gè)參數(shù)是一個(gè)謂詞,。find_if算法對(duì)輸入序列中的每個(gè)元素調(diào)用給定的這個(gè)謂詞。它返回第一個(gè)使謂詞返回非0值的元素,,如果不存在這樣的元素,,則返回尾后迭代器,。
// 獲取一個(gè)迭代器,,指向第一個(gè)滿足size() >= sz的元素
auto wc = find_if(
words.begin(),
words.end(),
[sz](const string &a){ return a.size() >= sz; });
-
對(duì)于一個(gè)對(duì)象或一個(gè)表達(dá)式,,如果可以對(duì)其使用調(diào)用運(yùn)算符,則稱它為可調(diào)用的,。包括函數(shù),、函數(shù)指針、重載了函數(shù)調(diào)用運(yùn)算符的類和lambda表達(dá)式,。
-
一個(gè)lambda表達(dá)式表示一個(gè)可調(diào)用的代碼單元,。我們可以將其理解為一個(gè)未命名的內(nèi)聯(lián)函數(shù)。lambda可能定義在函數(shù)內(nèi)部,。lambda必須使用尾置返回來(lái)指定返回類型:
[capture list](parameter list) -> return type { function body }
-
我們可以忽略參數(shù)列表和返回類型,,但必須永遠(yuǎn)包含捕獲列表和函數(shù)體。在lambda中忽略括號(hào)和參數(shù)列表等價(jià)于指定一個(gè)空參數(shù)列表,。如果忽略返回類型,,lambda根據(jù)函數(shù)體中的代碼推斷出返回類型。如果函數(shù)體只是一個(gè)return語(yǔ)句,,則返回類型從返回的表達(dá)式的類型推斷而來(lái),。否則返回類型為void。
-
lambda的調(diào)用方式與普通函數(shù)的調(diào)用方式相同,,都是使用調(diào)用運(yùn)算符,。
-
lambda不能有默認(rèn)參數(shù)。因此,,一個(gè)lambda調(diào)用的實(shí)參數(shù)目永遠(yuǎn)與形參數(shù)目相等,。
-
一個(gè)lambda只有在其捕獲列表中捕獲一個(gè)它所在函數(shù)中的局部變量,才能在函數(shù)體中使用該變量,。
-
for_each算法接受一個(gè)可調(diào)用對(duì)象,,并對(duì)輸入序列中每個(gè)元素調(diào)用此對(duì)象:
// 打印長(zhǎng)度大于等于給定值的單詞,每個(gè)單詞后面接一個(gè)空格
for_each(wc, words.end(), [](const string &s){ cout << s << " "; });
cout << endl;
-
捕獲列表只用于局部非static變量,,lambda可以直接使用局部static變量和在它所在函數(shù)之外聲明的名字,。
-
當(dāng)定義一個(gè)lambda時(shí),編譯器生成一個(gè)與lambda對(duì)應(yīng)的新的(未命名的)類類型,。默認(rèn)情況下,,從lambda生成的類都包含一個(gè)對(duì)應(yīng)該lambda所捕獲的變量的數(shù)據(jù)成員。類似任何普通類的數(shù)據(jù)成員,,lambda的數(shù)據(jù)成員也在lambda對(duì)象創(chuàng)建時(shí)被初始化,。
-
類似參數(shù)傳遞,變量的捕獲方式也可以是值或引用,。
-
采用值捕獲的前提是變量可以拷貝,。與參數(shù)不同,,被捕獲的變量的值是在lambda創(chuàng)建時(shí)拷貝,而不是調(diào)用時(shí)拷貝:
void fcn1()
{
size_t v1 = 42; // 局部變量
// 將v1拷貝到名為f的可調(diào)用對(duì)象
auto f = [v1] { return v1; };
v1 = 0;
auto j = f(); // j為42,;f保存了我們創(chuàng)建它時(shí)v1的拷貝
}
-
采用引用方式捕獲變量
void fcn2()
{
size_t v1 = 42; // 局部變量
// 對(duì)象f2包含v1的引用
auto f2 = [&v1]{ return v1; };
v1 = 0;
auto j = f2(); // j為0,;f2保存v1的引用,而非拷貝
}
void biggies(vector<string> &words,
vector<string>::size_type sz,
ostream &os = cout, char c = ' ')
{
// 與之前例子一樣的重排words的代碼
// 打印count的語(yǔ)句改為打印到os
for_each(words.begin(), words.end(),
[&os, c](const string &s){ os << s << c; });
}
- 如果函數(shù)返回一個(gè)lambda,,則與函數(shù)不能返回一個(gè)局部變量的引用類似,,此lambda也不能包含引用捕獲。
- 當(dāng)以引用方式捕獲一個(gè)變量時(shí),,必須保證在lambda執(zhí)行時(shí)變量是存在的,。
-
隱式捕獲:為了指示編譯器推斷捕獲列表,應(yīng)在捕獲列表中寫一個(gè)&或=,。&告訴編譯器采用捕獲引用方式,,=則表示采用值捕獲方式。
// sz為隱式捕獲,,值捕獲方式
wc = find_if(words.begin(), words.end(),
[=](const string &s){ return s.size() >= sz; });
可以混合使用隱式捕獲和顯式捕獲:
void biggies(vector<string> &words,
vector<string>::size_type sz,
ostream &os = cout, char c = ' ')
{
// 其他處理與前例一樣
// os隱式捕獲,,引用捕獲方式;c顯示捕獲,,值捕獲方式
for_each(words.begin(), words.end(),
[&, c](const string &s){ os << s << c; });
// os顯式捕獲,,引用捕獲方式;c隱式捕獲,,值捕獲方式
for_each(words.begin(), words.end(),
[=, &os](const string &s){ os << s << c; });
}
lambda捕獲列表 |
解釋 |
[] |
空捕獲列表,。lambda不能使用所在函數(shù)中的變量。一個(gè)lambda只有捕獲變量后才能使用它們 |
[names] |
names是一個(gè)逗號(hào)分隔的名字列表,,這些名字都是lambda所在函數(shù)的局部變量,。默認(rèn)情況下,捕獲列表中的變量都被拷貝,。名字前如果使用了&,,則采用引用捕獲方式 |
[&] |
隱式捕獲列表,采用引用捕獲方式,。lambda體中所使用的來(lái)自所在函數(shù)的實(shí)體都采用引用方式使用 |
[=] |
隱式捕獲列表,,采用值捕獲方式。lambda體將拷貝所使用的來(lái)自所在函數(shù)的實(shí)體的值 |
[&, identifier_list] |
identifier_list是一個(gè)逗號(hào)分隔的列表,,包含0個(gè)或多個(gè)來(lái)自所在函數(shù)的變量,。這些變量采用值捕獲方式,而任何隱式捕獲的變量都采用引用的方式捕獲,。identifier_list中的名字前面不能使用& |
[=, identifier_list] |
identifier_list中的變量都采用引用方式捕獲,,而任何隱式捕獲的變量都采用值方式捕獲。identifier_list中的名字不能包括this ,且這些名字之前必須使用& |
-
默認(rèn)情況下,,對(duì)于一個(gè)值被拷貝的變量,,lambda不會(huì)改變其值。如果我們希望能改變一個(gè)被捕獲的變量的值,,就必須在參數(shù)列表后加上關(guān)鍵字mutable ,。因此,可變lambda不能省略參數(shù)列表:
void fcn3()
{
size_t v1 = 42; // 局部變量
// f可以改變它所捕獲的變量的值
auto f = [v1] () mutable { return ++v1; }; // 不會(huì)影響外層v1的值
v1 = 0;
auto j = f(); // j為43
// 多次調(diào)用f,,lambda中v1的值會(huì)累加
// 在lambda中如果不加mutable,,則v1是read-only的
}
一個(gè)引用捕獲的變量是否(如往常一樣)可以修改依賴于此引用指向的是一個(gè)const類型還是一個(gè)非const類型:
void fcn4()
{
size_t v1 = 42; // 局部變量
// v1是一個(gè)非const變量的引用
// 可以通過(guò)f2中的引用來(lái)改變它
auto f2 = [&v1]{ return ++v1; }; // 外層v1值改變
v1 = 0;
auto j = f2();
}
-
transform接受三個(gè)迭代器和一個(gè)可調(diào)用對(duì)象,。前兩個(gè)迭代器表示輸入序列,,第三個(gè)迭代器表示目的位置。算法對(duì)輸入序列中每個(gè)元素調(diào)用可調(diào)用對(duì)象,,并將結(jié)果寫到目的位置,。目的位置迭代器與表示輸入序列開(kāi)始位置的迭代器可以是相同的。當(dāng)輸入迭代器和目的迭代器相同時(shí),,transform將輸入序列中每個(gè)元素替換為可調(diào)用對(duì)象操作該元素得到的結(jié)果,。
transform(vi.begin(), vi.end(), vi.begin(),
[](int i){ return i < 0 ? -i : i; });
-
count_if算法接受一對(duì)迭代器,表示一個(gè)輸入范圍,,還接受一個(gè)謂詞,,會(huì)對(duì)輸入范圍中每個(gè)元素執(zhí)行。count_if返回一個(gè)計(jì)數(shù)值,,表示謂詞有多少次為真,。
-
在很多地方使用相同的操作,或者一個(gè)操作需要很多語(yǔ)句才能完成,,通常使用函數(shù)比lambda表達(dá)式更好,。
-
bind標(biāo)準(zhǔn)庫(kù)函數(shù)定義在頭文件functional中??梢詫ind函數(shù)看作一個(gè)通用的函數(shù)適配器,,它接受一個(gè)可調(diào)用對(duì)象,生成一個(gè)新的可調(diào)用對(duì)象來(lái)“適應(yīng)”原對(duì)象的參數(shù)列表,。
-
調(diào)用bind的一般形式為:auto newCallable = bind(callable, arg_list); 其中,,newCallable本身是一個(gè)可調(diào)用對(duì)象,arg_list是一個(gè)逗號(hào)分隔的參數(shù)列表,,對(duì)應(yīng)給定的callable的參數(shù),。即,當(dāng)我們調(diào)用newCallable時(shí),,newCallable會(huì)調(diào)用callable,,并傳遞給它arg_list中的參數(shù)。
-
arg_list中的參數(shù)可能包含形如_n的名字,,其中n是一個(gè)整數(shù),。這些參數(shù)是”占位符“,,表示newCallable的參數(shù),它們占據(jù)了傳遞給newCallable的參數(shù)的”位置“,。數(shù)值n表示生成的可調(diào)用對(duì)象中參數(shù)的位置:_1為newCallable的第一個(gè)參數(shù),,_2為第二個(gè)參數(shù),以此類推,。
// g是一個(gè)有兩個(gè)參數(shù)的可調(diào)用對(duì)象
auto g = bind(f, a, b, _2, c, _1); // 調(diào)用g(X,Y)會(huì)調(diào)用 f(a, b, Y, c, X)
// 可用bind重排參數(shù)順序
-
名字_n都定義在一個(gè)名為placeholders的命名空間中,,而這個(gè)命名空間本身定義在std命名空間中。using namespace std::placeholders;
-
默認(rèn)情況下,,bind的那些不是占位符的參數(shù)被拷貝到bind返回的可調(diào)用對(duì)象中,,但是,與lambda類似,,有時(shí)對(duì)有些綁定的參數(shù)我們希望以引用方式傳遞,,或是要綁定參數(shù)的類型無(wú)法拷貝,就必須使用標(biāo)準(zhǔn)庫(kù)ref函數(shù):
// 1
for_each(words.begin(), words.end(), [&os, c](const string &s){ os << s << c; });
// 2
ostream &print(ostream &os, const string &s, char c)
{
return os << s << c;
}
for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));
-
函數(shù)ref返回一個(gè)對(duì)象,,包含給定的引用,,此對(duì)象是可以拷貝的。標(biāo)準(zhǔn)庫(kù)中還有一個(gè)cref函數(shù),,生成一個(gè)保存const引用的類,。與bind一樣,函數(shù)ref和cref也定義在頭文件functional中,。
插入迭代器操作 |
解釋 |
it=t |
在it指定的當(dāng)前位置插入值t,。假定c是it綁定的容器,依賴于插入迭代器的不同種類,,此賦值會(huì)分別調(diào)用c.push_back(t),、c.push_front(t)或c.insert(t,p),其中p為傳遞給inserter的迭代器位置 |
*it,++it,it++ |
這些操作雖然存在,,但不會(huì)對(duì)it做任何事情,。每個(gè)操作都返回it(所以*it++ = t; 等價(jià)于it = t; ) |
-
插入器有三種類型,差異在于元素插入的位置:
- back_inserter創(chuàng)建一個(gè)使用push_back的迭代器
- front_inserter創(chuàng)建一個(gè)使用push_front的迭代器
- inserter創(chuàng)建一個(gè)使用insert的迭代器,。此函數(shù)接受第二個(gè)參數(shù),,這個(gè)參數(shù)必須是一個(gè)指向給定容器的迭代器。元素將被插入到給定迭代器所表示的元素之前,。
-
只有在容器支持push_front的情況下,,我們才可以使用front_inserter。類似的,,只有在容器支持push_back的情況下,,我們才能使用back_inserter。
-
當(dāng)調(diào)用inserter(c, iter)時(shí),我們得到一個(gè)迭代器,,接下來(lái)使用它時(shí),,會(huì)將元素插入到iter原來(lái)所指向的元素之前的位置。即,,如果it是由inserter生成的迭代器,,則下面這樣的賦值語(yǔ)句
*it = val;
其效果與下面代碼一樣:
it = c.insert(it, val); // it指向新加入的元素
++it; // 遞增it使它指向原來(lái)的元素
-
unique_copy函數(shù)接受第三個(gè)迭代器,表示拷貝不重復(fù)元素的目的位置,。與unique一樣,,要求重復(fù)元素相鄰存放。
-
對(duì)于一個(gè)綁定到流的迭代器,,一旦其關(guān)聯(lián)的流遇到文件尾或遇到IO錯(cuò)誤,,迭代器的值就與尾后迭代器相等。
istream_iterator<int> in_iter(cin), eof; // 從cin讀取int
vector<int> vec(in_iter, eof); // 從迭代器范圍構(gòu)造vec
istream_iterator操作 |
解釋 |
istream_iterator in(is); |
in從輸入流is讀取類型為T的值 |
istream_iterator end; |
讀取類型為T的值的istream_iterator迭代器,,表示尾后位置 |
in1 == in2 |
in1和in2必須讀取相同類型,。如果它們都是尾后迭代器,,或綁定到相同的輸入,,則兩者相等 |
*in |
返回從流中讀取的值 |
in->mem |
與(*in).mem的含義相同 |
++in, in++ |
使用元素類型所定義的>>運(yùn)算符從輸入流中讀取下一個(gè)值。與以往一樣,,前置版本返回一個(gè)指向遞增后迭代器的引用,,后置版本返回舊值 |
- istream_iterator允許使用懶惰求值:標(biāo)準(zhǔn)庫(kù)并不保證迭代器立即從流讀取數(shù)據(jù)。具體實(shí)現(xiàn)可以推遲從流中讀取數(shù)據(jù),,直到我們使用迭代器時(shí)才真正讀取,。標(biāo)準(zhǔn)庫(kù)中的實(shí)現(xiàn)所保證的是,在我們第一次解引用迭代器之前,,從流中讀取數(shù)據(jù)的操作已經(jīng)完成了,。
ostream_iterator操作 |
解釋 |
ostream_iterator out(os); |
out將類型為T的值寫到輸出流os中 |
ostream_iterator out(os, d); |
out將類型為T的值寫到輸出流os中,每個(gè)值后面都輸出一個(gè)d,。d指向一個(gè)空字符結(jié)尾的字符數(shù)組 |
out = val |
用<<運(yùn)算符將val寫入到out所綁定的ostream中,。val的類型必須與out可寫的類型兼容 |
*out, ++out, out++ |
這些運(yùn)算符時(shí)存在的,但不對(duì)out做任何事情,。每個(gè)運(yùn)算符都返回out |
-
我們可以用ostream_iterator來(lái)輸出值的序列:
ostream_iterator<int> out_iter(cout, " ");
for(auto e : vec)
*out_iter++ = e; // 賦值語(yǔ)句實(shí)際上將元素寫到cout
cout << endl;
當(dāng)我們向out_iter賦值時(shí),,可以忽略解引用和遞增運(yùn)算。
for(auto e : vec)
out_iter = e; // 賦值語(yǔ)句將元素寫到cout
cout << endl;
通過(guò)調(diào)用copy來(lái)打印vec中的元素,,這比編寫循環(huán)更為簡(jiǎn)單:
copy(vec.begin(), vec.end(), out_iter);
cout << endl;
-
我們可以為任何定義了輸入運(yùn)算符(>>)的類型創(chuàng)建istream_iterator對(duì)象,。類似的,只要類型有輸出運(yùn)算符(<<),,我們就可以為其定義ostream_iterator,。
-
除了forward_list之外,其他容器都支持反向迭代器。我們可以通過(guò)調(diào)用rbegin,、rend,、crbegin、crend成員函數(shù)來(lái)獲得反向迭代器,。這些成員函數(shù)返回指向容器尾元素和首元素之前一個(gè)位置的迭代器,。與普通迭代器一樣,反向迭代器也有const和非const版本,。
-
通過(guò)向sort傳遞一對(duì)反向迭代器來(lái)將vector整理為遞減序:
sort(vec.begin(), vec.end()); // 按“正常序”排序vec
// 按逆序排序:將最小元素放在vec的末尾
sort(vec.rbegin(), vec.rend());
-
只能從即支持++也支持--的迭代器來(lái)定義反向迭代器,。流迭代器不支持遞減運(yùn)算,因?yàn)椴豢赡茉谝粋€(gè)流中反向移動(dòng),。因此,,不可能從一個(gè)forward_list或一個(gè)流迭代器創(chuàng)建反向迭代器。
-
reverse_iterator的base成員函數(shù)將反向迭代器轉(zhuǎn)換并返回一個(gè)普通迭代器,。
// 在一個(gè)逗號(hào)分隔的列表中查找第一個(gè)元素
auto comma = find(line.cbegin(), line.cend(), ',');
cout << string(line.cbegin(), comma) << endl;
// 在一個(gè)逗號(hào)分隔的列表中查找最后一個(gè)元素
auto rcomma = find(line.crbegin(), line.crend(), ',');
// 錯(cuò)誤:將逆序輸出單詞的字符
cout << string(line.crbegin(), rcomma) << endl;
// 正確:得到一個(gè)正向迭代器,,從逗號(hào)后開(kāi)始讀取字符直到line末尾
cout << string(rcomma.base(), line.cend()) << endl;
-
反向迭代器的目的是表示元素范圍,而這些范圍是不對(duì)稱的,,這導(dǎo)致一個(gè)重要的結(jié)果:當(dāng)我們從一個(gè)普通迭代器初始化一個(gè)反向迭代器,,或是給一個(gè)反向迭代器賦值時(shí),結(jié)果迭代器與原迭代器指向的并不是相同的元素,。
-
迭代器類別:
- 輸入迭代器:只讀,,不寫;單邊掃描,,只能遞增(++),;還支持相等性判定運(yùn)算符(==、!=),、解引用運(yùn)算符(*)(只出現(xiàn)在賦值運(yùn)算符右側(cè))和箭頭運(yùn)算符(->),。
- 輸入迭代器只用于順序訪問(wèn)。
- 對(duì)于一個(gè)輸入迭代器,,*it++保證是有效的,,但遞增它可能導(dǎo)致所有其他指向流的迭代器失效。其結(jié)果就是,,不能保證輸入迭代器的狀態(tài)可以保存下來(lái)并用來(lái)訪問(wèn)元素,。因此,輸入迭代器只能用于單邊掃描算法,。
- 算法find和accumulate要求輸入迭代器,;而istream_iterator是一種輸入迭代器。
- 輸出迭代器:只寫,,不讀,;單邊掃描,,只能遞增(++),支持解引用運(yùn)算符(*)(只出現(xiàn)在賦值運(yùn)算符左側(cè)),。
- 我們只能向一個(gè)輸出迭代器賦值一次,。
- 只能用于單邊掃描算法。
- copy函數(shù)的第三個(gè)參數(shù)就是輸出迭代器,。ostream_iterator類型也是輸出迭代器,。
- 前向迭代器:可讀寫;多遍掃描,,只能遞增(++),,支持所有輸入、輸出迭代器的操作,。
- 雙向迭代器:可讀寫,;多遍掃描,可遞增遞減(++,、--),,支持所有前向迭代器操作。
- 隨機(jī)訪問(wèn)迭代器:可讀寫,,多遍掃描,,支持全部迭代器運(yùn)算,除了上述迭代器類別支持的操作外,,還有比較兩個(gè)迭代器相對(duì)位置的關(guān)系運(yùn)算符(<,、<=、>,、>=)、迭代器和一個(gè)整數(shù)值的加減運(yùn)算(+,、+=,、-、-=)令迭代器在序列中前進(jìn)或后退給定整數(shù)個(gè)元素,、兩個(gè)迭代器上的減法運(yùn)算符(-)得到其距離以及下標(biāo)運(yùn)算符(iter[n]與*(iter+n)等價(jià)),。
- 提供在常量時(shí)間內(nèi)訪問(wèn)序列中任意元素的能力。
-
除了輸出迭代器之外,,一個(gè)高層類別的迭代器支持底層類別迭代器的所有操作,。C++標(biāo)準(zhǔn)指明了泛型和數(shù)值算法的每個(gè)得到其參數(shù)的最小迭代器類別。對(duì)每個(gè)迭代器參數(shù)來(lái)說(shuō),,其能力必須與規(guī)定的最小類別至少相當(dāng),。向算法傳遞一個(gè)能力更差的迭代器會(huì)產(chǎn)生錯(cuò)誤。
-
算法形參模式:
- alg(beg, end, other args);
- alg(beg, end, dest, other args);
- alg(beg, end, beg2, other args);
- alg(beg, end, beg2, end2, other args);
beg和end表示算法所操作的輸入范圍
dest參數(shù)是一個(gè)表示算法可以寫入的目的位置的迭代器
接受單獨(dú)的beg2或是接受beg2和end2的算法用這些迭代器表示第二個(gè)輸入范圍
other args表示一些算法還接受額外的,、非迭代器的特定參數(shù)
-
算法命名規(guī)范:
-
一些算法使用重載形式傳遞一個(gè)謂詞
unique(beg, end); // 使用==運(yùn)算符比較元素
unique(beg, end, comp); // 使用comp比較元素
-
_if版本的算法
find(beg, end, val); // 查找輸入范圍中val第一次出現(xiàn)的位置
find_if(beg, end, pred); // 查找第一個(gè)令pred為真的元素
-
_copy區(qū)分拷貝元素的版本和不拷貝的版本
reverse(beg, end); // 反轉(zhuǎn)輸入范圍中元素的順序
reverse_copy(beg, end, dest); // 將元素按逆序拷貝到dest
-
一些算法同時(shí)提供_copy和_if版本
// 從v1中刪除奇數(shù)元素
remove_if(v1.begin(), v1.end(),
[](int i){ return i % 2; });
// 將偶數(shù)元素從v1拷貝到v2,;v1不變
remove_copy_if(v1.begin(), v1.end(), back_inserter(v2),
[](int i){ return i % 2; });
-
鏈表版本的算法性能比對(duì)應(yīng)的通用版本好得多,,對(duì)于list和forward_list,應(yīng)該優(yōu)先使用成員函數(shù)版本的算法而不是通用算法,。
list和forward_list成員函數(shù)版本的算法 |
解釋 |
—— |
這些操作都返回void |
lst.merge(lst2)或lst.merge(lst2, comp) |
將來(lái)自lst2的元素合并入lst,。lst和lst2都必須是有序的。元素將從lst2中刪除,。在合并之后,,lst2變?yōu)榭铡5谝粋€(gè)版本使用<運(yùn)算符,;第二個(gè)版本使用給定的比較操作 |
lst.remove(val)或lst.remove_if(pred) |
調(diào)用erase刪除掉與給定值相等(==)或令一元謂詞為真的每個(gè)元素 |
lst.reverse() |
反轉(zhuǎn)lst中元素的順序 |
lst.sort()或lst.sort(comp) |
使用<或給定比較操作順序排序元素 |
lst.unique()或lst.unique(pred) |
調(diào)用erase刪除同一個(gè)值的連續(xù)拷貝,。第一個(gè)版本使用==;第二個(gè)版本使用給定的二元謂詞 |
list和forward_list的splice成員函數(shù)的參數(shù) |
解釋 |
—— |
lst.splice(args)或flst.splice_after(args) |
(p, lst2) |
p是一個(gè)指向lst中元素的迭代器,,或一個(gè)指向flst首前位置的迭代器,。函數(shù)將lst2的所有元素移動(dòng)到lst中p之前的位置或者是flst中p之后的位置。將元素從lst2中刪除,。lst2的類型必須與lst或flst相同,,且不能是同一個(gè)鏈表 |
(p, lst2, p2) |
p2是一個(gè)指向lst2中位值的有效的迭代器。將p2指向的元素移動(dòng)到lst中,,或?qū)2之后的元素移動(dòng)到flst中,。lst2可以是與lst或flst相同的鏈表 |
(p, lst2, b, e) |
b和e必須表示lst2中的合法范圍。將給定范圍中的元素從lst2移動(dòng)到lst或flst,。lst2與lst(或flst)可以是相同的鏈表,,但p不能指向給定范圍中元素。 |
- 鏈表特有版本與通用版本間的一個(gè)至關(guān)重要的區(qū)別是鏈表版本會(huì)改變底層的容器,。
|