24. STL是一種泛型編程,面向?qū)ο缶幊剃P(guān)注的是編程的數(shù)據(jù)方面,而泛型編程關(guān)注的是算法方面驶悟。它們之間的共同點(diǎn)是抽象可重用的代碼,但它們的理念完全不同材失。
25. 模板使得算法獨(dú)立于存儲(chǔ)的數(shù)據(jù)類型持钉,而迭代器使算法獨(dú)立于使用的容器類型大州。
26. 為區(qū)分++運(yùn)算符的前綴版本和后綴版本,C++將operator++作為前綴版本,將operator++(int)作為后綴版本叨粘;其中的參數(shù)永遠(yuǎn)不會(huì)被用到,所以不必指定其名稱鸥跟。
P686 iterator類的實(shí)例揽惹。
27. STL定義了5種迭代器,輸入迭代器秸弛、輸出迭代器铭若、正向迭代器、雙向迭代器和隨機(jī)訪問迭代器递览。
28. 輸入迭代器可被程序用來讀取容器中的信息叼屠,需要輸入迭代器的算法將不會(huì)修改容器中的值。輸入迭代器必須能夠訪問容器中的所有值绞铃,這是通過支持++運(yùn)算符來實(shí)現(xiàn)的镜雨。順便說一句,并不能保證輸入迭代器第二次遍歷容器時(shí)儿捧,順序不變荚坞。另外挑宠,輸入迭代器被遞增后,也不能保證其先前的值仍然可以被解除引用西剥”云埽基于輸入迭代器的任何算法都應(yīng)該是單通行的,不依賴于前一次遍歷時(shí)的迭代器值瞭空,也不依賴于本次遍歷時(shí)前面的迭代器值揪阿。
輸入迭代器是單向迭代器,可以遞增咆畏,但不能倒退南捂。
29. 輸出迭代器與輸入迭代器相似,只是解除引用讓程序能夠修改容器值旧找,而不能讀取溺健。簡(jiǎn)而言之,對(duì)于單通行钮蛛、只讀算法鞭缭,可以使用輸入迭代器;而對(duì)于單通行魏颓、只寫算法岭辣,則可以使用輸出迭代器。
30. 與輸入迭代器和輸出迭代器相似甸饱,正向迭代器只是用++運(yùn)算符來遍歷容器沦童,所以它每次沿容器向前移動(dòng)一個(gè)元素;然而叹话,與輸入和輸出迭代器不同的是偷遗,它總是按相同的順序遍歷一系列值。另外驼壶,將正向迭代器遞增后氏豌,仍然可以對(duì)前面的迭代器值解除引用(如果保存了它),并可以得到相同的值热凹。這些特征使得多次通行算法稱為可能箩溃。
正向迭代器既可以使得能夠讀取和修改數(shù)據(jù),也可以使得只能讀取數(shù)據(jù)碌嘀。
int * pirw;//可讀可寫
const int * pir;//只寫
31. 雙向迭代器具有正向迭代器的所有特性涣旨,同時(shí)支持兩種(前綴和后綴)遞減運(yùn)算符。例如:reverse函數(shù)可以交換第一個(gè)元素和最后一個(gè)元素股冗、將指向第一個(gè)元素的指針加1霹陡、將指向第二個(gè)元素的指針減1,并重復(fù)這種處理過程。
32. 有些算法(如標(biāo)準(zhǔn)排序和二分檢索)要求能夠直接跳到容器中的任何一個(gè)元素烹棉,這叫做隨機(jī)訪問攒霹,需要隨機(jī)訪問迭代器。隨機(jī)訪問迭代器具有雙向迭代器的所有特性浆洗,同時(shí)增加了支持隨機(jī)訪問的操作(如指針增加運(yùn)算)和用于對(duì)迭代器進(jìn)行比較的關(guān)系運(yùn)算符催束。P689表16.3為隨機(jī)訪問迭代器操作。
33. 迭代器層次結(jié)構(gòu):正向迭代器具有輸入迭代器和輸出迭代器的全部功能伏社,同時(shí)還有自己的功能抠刺;雙向迭代器具有正向迭代器的全部功能,同時(shí)還有自己的功能摘昌;隨機(jī)訪問迭代器具有正向迭代器的全部功能速妖,同時(shí)還有自己的功能。
34. 根據(jù)特定迭代器類型編寫的算法可以使用該種迭代器聪黎,也可以使用具有所需功能的任何其他迭代器罕容。所以具有隨機(jī)訪問迭代器的容器可以使用為輸入迭代器編寫的算法。
35. 為何需要這么多迭代器呢稿饰?目的是為了在編寫算法盡可能使用要求最低的迭代器锦秒,并讓它適用于容器的最大區(qū)間。例如喉镰,vector<int>::iterator是隨機(jī)訪問迭代器旅择,它允許使用基于任何迭代器類型的算法,因?yàn)殡S機(jī)訪問迭代器具有所有迭代器的功能梧喷。list<int>::iterator它使用雙向迭代器,因此不能使用基于隨機(jī)訪問存儲(chǔ)器的算法脖咐,但可以使用基于要求較低的迭代器算法铺敌。
36.迭代器種類表示一種概念(concept),而不是類型屁擅,概念用來描述一系列要求偿凭。正向迭代器是一系列要求,而不是類型派歌。概念具有類似繼承的關(guān)系弯囊,例如,雙向迭代器繼承了正向迭代器的功能胶果。然而匾嘱,不能將繼承機(jī)制用于迭代器。例如早抠,可以將正向迭代器實(shí)現(xiàn)為一個(gè)類霎烙,而將雙向迭代器實(shí)現(xiàn)為一個(gè)常規(guī)指針。然而,從概念上看悬垃,它確實(shí)能夠繼承游昼。有些STL文獻(xiàn)使用術(shù)語改進(jìn)(refinement)來表示這種概念上的繼承,因此尝蠕,雙向迭代器是對(duì)正向迭代器概念的一種改進(jìn)烘豌。
概念的具體實(shí)現(xiàn)被稱為模型(model)。因此看彼,指向int的常規(guī)指針是一個(gè)隨機(jī)訪問迭代器模型廊佩,也是一個(gè)正向迭代器模型,因?yàn)樗鼭M足該概念的所有要求闲昭。
37. 迭代器是廣義指針罐寨,而指針滿足所有迭代器的要求。由于指針是迭代器序矩,而STL算法是基于迭代器的鸯绿,這使得可將STL算法用于常規(guī)數(shù)組。
38. copy()函數(shù)將數(shù)據(jù)從一個(gè)容器中復(fù)制到另一個(gè)容器中簸淀。
? int casts[10] = {6, 7, 3, 9, 5, 11, 8, 7, 10, 5};
? vector<int> dice(10);
? copy(casts, casts + 10, dice.begin());
? 前兩個(gè)迭代器參數(shù)表示要復(fù)制的范圍瓶蝴,最后一個(gè)迭代器參數(shù)表示要將第一個(gè)元素復(fù)制到什么位置。copy()函數(shù)將覆蓋目標(biāo)容器中已有的數(shù)據(jù)租幕,同時(shí)目標(biāo)容器必須足夠大舷手,以便能夠容納被復(fù)制的元素。因此劲绪,不能使用copy()函數(shù)將數(shù)據(jù)放到空矢量中男窟。
39. ostream_iterator模板是輸出迭代器概念的一個(gè)模型,它也是一個(gè)適配器——一個(gè)類或函數(shù)贾富,可以將一些其他接口轉(zhuǎn)換為STL使用的接口歉眷。可以通過包含頭文件iterator并作下面的聲明來創(chuàng)建這種迭代器:
#include <iterator>
ostream_iterator<int, char> out_iter(cout, “ “);
第一個(gè)模板參數(shù)int指出了被發(fā)送給輸出流的數(shù)據(jù)類型颤枪;第二個(gè)模板參數(shù)char指出了輸出流使用的字符類型(另一個(gè)是wchar_t)汗捡。構(gòu)造函數(shù)的第一個(gè)參數(shù)cout指出了要使用的輸出流,最后一個(gè)字符串參數(shù)是在發(fā)送給輸出流的每個(gè)數(shù)據(jù)項(xiàng)后顯示的分隔符畏纲。
copy(dice.begin(), dice.end(), out_iter);意味著顯示容器的內(nèi)容扇住。
40. iterator頭文件還定義了istream_iterator模板,使istream輸入可用作迭代器接口盗胀。它是一個(gè)輸入迭代器概念的一個(gè)模型艘蹋。
copy(istream_iterator<int, char> (cin), istream_iterator<int, char>(), dice.begin());
istream_iterator使用兩個(gè)模板參數(shù),第一個(gè)參數(shù)指出要讀取的數(shù)據(jù)類型票灰,第二個(gè)參數(shù)指出輸入流使用的字符類型簿训。使用構(gòu)造函數(shù)cin表示使用由cin管理的輸入流咱娶,省略構(gòu)造函數(shù)表示輸入失敗,因此上述代碼從輸入流中讀取强品,直到文件結(jié)尾膘侮、類型不匹配或出現(xiàn)其它輸入故障為止。
41. iterator頭文件還提供了reverse_iterator的榛、back_insert_iterator琼了、front_insert_iterator和insert_iterator。
42. 反向輸出
? ostream_iterator<int, char> out_iter(cout, “ “);
? copy(dice.rbegin(), dice.rend(), out_iter);
? vector類有一個(gè)名為rbegin的成員函數(shù)和一個(gè)名為rend()的成員函數(shù)夫晌,前者返回一個(gè)指向超尾的反向迭代器雕薪,后者返回一個(gè)指向第一個(gè)元素的反向迭代器。因此對(duì)迭代器執(zhí)行遞增操作將導(dǎo)致它被遞減晓淀。
rbegin()和end()返回相同的值所袁,但類型不同(reverse_iterator和iterator)。同樣凶掰,rend()和begin()也返回相同的值(指向第一個(gè)元素的迭代器)燥爷,但類型不同。
43. 反向指針的內(nèi)部實(shí)現(xiàn)是通過將指針遞減懦窘,再解除引用前翎。
44. 三種迭代器back_insert_iterator、front_insert_iterator畅涂、insert_iterator港华。
copy(cats, cats + 10, dice.begin());
這些值將覆蓋dice中以前的內(nèi)容,且該函數(shù)假設(shè)dice有足夠的空間午衰,能夠容納這些值立宜,即copy()不能自動(dòng)根據(jù)發(fā)送值調(diào)整目標(biāo)容器的長度。如果要將元素添加到dice中臊岸,而不是覆蓋已有的內(nèi)容橙数,就要使用三種插入迭代器。
back_insert_iterator只能用于允許在尾部快速插入的容器(快速插入指的是一個(gè)時(shí)間固定的算法)扇单,vector符合這種要求商模。front_insert_iterator只能用于允許在起始位置做時(shí)間固定插入的容器類型奠旺,vector類不能滿足這種要求蜘澜,但queue滿足。insert_iterator沒有這些要求响疚,因此可以用它把信息插入到矢量的前端鄙信。然而,front_insert_iterator對(duì)于支持它的容器來說忿晕,完成任務(wù)的速度更快装诡。
back_insert_iterator<vector<int>> back_insert(dice);
必須聲明容器類型的原因是,迭代器必須使用合適的容器方法。copy()函數(shù)是一個(gè)獨(dú)立的函數(shù)鸦采,沒有調(diào)整容器大小的權(quán)限宾巍。但前面的聲明讓back_iter能夠使用方法vector<int>::push_back,該方法有這樣的權(quán)限渔伯。
聲明front_insert_iterator的方式與此相同顶霞。對(duì)于insert_iterator聲明,還需一個(gè)指示插入位置的構(gòu)造函數(shù):
insert_iterator<vector<int> > insert_iter(dice, dice.begin());
45. STL容器有deque锣吼、list选浑、queue、priority_queue玄叠、stack古徒、vector、map读恃、multimap隧膘、set、multiset和bitset(比特級(jí)處理數(shù)據(jù)的容器)狐粱;C++11新增加了forward_list舀寓、unordered_map、unordered_multimap肌蜻、unordered_set和unordered_multiset互墓,且不將bitset視為容器,而將其視為一種獨(dú)立的類別蒋搜。
46. 容器是存儲(chǔ)其它對(duì)象的對(duì)象篡撵,被存儲(chǔ)的對(duì)象必須是同一種類型的。存儲(chǔ)在容器中的數(shù)據(jù)為容器所有豆挽,這意味著當(dāng)容器過期時(shí)育谬,存儲(chǔ)在容器中的數(shù)據(jù)也將過期(然而,如果是指針的話帮哈,則它指向的數(shù)據(jù)并不一定過期)膛檀。
47. 不能將任意類型的對(duì)象存儲(chǔ)在容器中,具體地說娘侍,類型必須是可復(fù)制構(gòu)造的和可賦值的咖刃。基本類型滿足這些要求憾筏;只要類定義沒有將復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符聲明為私有或保護(hù)的嚎杨,則也滿足這種要求。
48. 復(fù)制構(gòu)造和復(fù)制賦值以及移動(dòng)構(gòu)造和移動(dòng)賦值之間的差別在于氧腰,復(fù)制操作保留源對(duì)象枫浙,而移動(dòng)操作可修改源對(duì)象刨肃,還可能轉(zhuǎn)讓所有權(quán),而不做任何復(fù)制箩帚。如果源對(duì)象是臨時(shí)的真友,移動(dòng)操作的效率將高于常規(guī)復(fù)制。
49. a[n]和a.at(n)適用于vector和deque容器紧帕。它們之間的區(qū)別是锻狗,如果n落在容器的有效區(qū)間外,則a.at(n)將執(zhí)行邊界檢查焕参,并引發(fā)out_of_range異常轻纪。
50. vector是數(shù)組的一種類表示。它提供了對(duì)元素的隨機(jī)訪問叠纷。在尾部添加和刪除元素的時(shí)間是固定的刻帚,但在頭部或中間插入和刪除元素的復(fù)雜度為線性時(shí)間。
51. deque表示雙端隊(duì)列(double-ended queue)涩嚣,通常為deque崇众。在STL中,其實(shí)現(xiàn)類似于vector容器航厚,支持隨機(jī)訪問顷歌。主要區(qū)別在于,從deque對(duì)象的開始位置插入和刪除元素的時(shí)間是固定的幔睬,而不像vector中那樣是線性時(shí)間的眯漩。所以,如果多數(shù)操作發(fā)生在序列的起始和結(jié)尾處麻顶,則應(yīng)考慮使用deque數(shù)據(jù)結(jié)構(gòu)赦抖。
為實(shí)現(xiàn)在deque兩端執(zhí)行插入和刪除操作為固定時(shí)間的這一目的,deque對(duì)象的設(shè)計(jì)比vector對(duì)象更為復(fù)雜辅肾。因此队萤,盡管兩者都提供對(duì)元素的隨機(jī)訪問和在序列中部執(zhí)行線性時(shí)間的插入和刪除操作,但vector容器執(zhí)行這些操作時(shí)速度更快些矫钓。
52. list模板類表示雙向鏈表要尔。list在鏈表中的任意位置進(jìn)行插入和刪除的時(shí)間都是固定的。它與vector的區(qū)別是vector強(qiáng)調(diào)的是通過隨機(jī)訪問進(jìn)行快速訪問新娜,而list強(qiáng)調(diào)的是元素的快速插入和刪除赵辕。
53. 與vector迭代器不同,從list中插入或刪除元素后杯活,list迭代器指向的元素將不變匆帚。
54. list部分成員函數(shù):P699
? void merge(list<T, Alloc> &x);將兩個(gè)有序鏈表合并熬词。合并后經(jīng)過排序的鏈表保存在調(diào)用鏈表中旁钧,x為空吸重。
? void remove(const T & val);
? void sort();
? void splice(iterator pos, list<T, Alloc> x);將鏈表x的內(nèi)容插入到pos的前面歪今,x將為空嚎幸。
? void unique()將連續(xù)的相同元素壓縮為單個(gè)元素。
55. list的成員函數(shù)insert()和splice()之間的主要區(qū)別是:insert()將元素區(qū)間的副本插入到目標(biāo)地址寄猩,而splice()則將原始區(qū)間移到目標(biāo)地址嫉晶。另一個(gè)區(qū)別是參數(shù)個(gè)數(shù)和類型。
56. 非成員sort()函數(shù)田篇,但它需要隨機(jī)訪問迭代器替废。所以不能將非成員函數(shù)sort()用于鏈表。
57. list方法組成了一個(gè)非常有用的工具箱泊柬。例如椎镣,假設(shè)有兩個(gè)郵件列表要整理,則可以對(duì)每個(gè)列表進(jìn)行排序兽赁,合并它們状答,然后使用unique()來刪除重復(fù)元素。
58. C++11新增加了forward_list刀崖,它實(shí)現(xiàn)了單鏈表惊科。不同于vector和list,forward_list是不可反轉(zhuǎn)的容器亮钦。相比于list馆截,forward_list更簡(jiǎn)單、更緊湊蜂莉,但功能更少孙咪。
59. priority_queue默認(rèn)情況下,最大的元素被移到隊(duì)首巡语。
60. 模板類array并非STL容器翎蹈,因?yàn)槠溟L度是固定的。因此男公,array沒有定義調(diào)整容器大小的操作荤堪,但定義了對(duì)它來說有意義的成員函數(shù),如operator[]()和at()枢赔〕窝簦可將很多標(biāo)準(zhǔn)STL算法用于array對(duì)象。
61. P703非成員函數(shù)set_union()踏拜、set_intersection()和set_difference()方法碎赢。
62. 兩個(gè)有用的set方法是lower_bound()和upper_bound()。方法lower_bound將鍵作為參數(shù)并返回一個(gè)迭代器速梗,該迭代器返回指向集合中第一個(gè)不小于鍵參數(shù)的成員肮塞。方法upper_bound()將鍵作為參數(shù)襟齿,并返回一個(gè)迭代器,該迭代器返回集合中第一個(gè)不小于鍵參數(shù)的成員枕赵。
63. 因?yàn)榕判驔Q定了要插入的位置猜欺,所以set的insert方法只指定插入額信息。
string s(“tennis”);
A. insert(s);
B. insert(A.begin(), A.end());
64. multimap<int, string> codes;
? pair<cons tint, string> item(213, ”Los Angeles”);
? codes.insert(item);
? 對(duì)于pair對(duì)象拷窜,可以使用first和second成員來訪問其兩個(gè)句子:
cout << item.first << ‘ ‘ << item.second << endl;
65. multimap成員函數(shù)count()接受鍵作為參數(shù)开皿,并返回具有該鍵的元素?cái)?shù)目。成員函數(shù)lower_bound()和upper_bound()將鍵作為參數(shù)篮昧,工作原理與set時(shí)相同赋荆。成員函數(shù)equal_range()用鍵作為參數(shù),且返回兩個(gè)迭代器懊昨,它們表示的區(qū)間與該鍵匹配糠睡。為返回兩個(gè)值,該方法將它們封裝在pair對(duì)象中疚颊,這里pair的兩個(gè)模板參數(shù)都是迭代器狈孔。
66. 無序關(guān)聯(lián)容器unordered_set、unordered_multiset材义、unordered_map均抽、unordered_multimap。底層的區(qū)別是關(guān)聯(lián)容器是基于樹結(jié)構(gòu)的其掂,而無序關(guān)聯(lián)容器是基于數(shù)據(jù)結(jié)構(gòu)哈希表的油挥。
67. 函數(shù)對(duì)象(functor,也稱函數(shù)符)是可以以函數(shù)形式與()結(jié)合使用的任意對(duì)象款熬。這包括函數(shù)名深寥、指向函數(shù)的指針和重載了()運(yùn)算符的類對(duì)象。
68. for_each(books.begin(), books.end(), ShowReview);
? 第三個(gè)參數(shù)可以是常規(guī)函數(shù)贤牛,也可以是函數(shù)對(duì)象惋鹅。for_each的原型是:
template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);
69. 生成器(generator)是不用參數(shù)就可以調(diào)用的函數(shù)符。
? 一元函數(shù)(unary function)是用一個(gè)參數(shù)可以調(diào)用的函數(shù)符殉簸。
? 二元函數(shù)(binary function)是用兩個(gè)參數(shù)可以調(diào)用的函數(shù)符闰集。
? 返回bool值的一元函數(shù)是謂詞(predicate);
? 返回bool值的二元函數(shù)是二元謂詞(binary predicate)般卑。
70. 一些STL函數(shù)需要謂詞參數(shù)或二元謂詞參數(shù)武鲁。例如,sort將二元謂詞作為其第三個(gè)參數(shù):
bool WorseThan(const Review & r1, const Review & r2)
……
sort(books.begin(), books.end(), WorseThan);
71. list模板有一個(gè)將謂詞作為參數(shù)的remove_if()成員蝠检,該函數(shù)將謂詞用于區(qū)間的每一個(gè)元素沐鼠,如果謂詞返回true,則刪除這些元素。
bool tooBig(int n) {return n > 100;}
list<int> scores;
…
scores.remove_if(tooBig);
假如要?jiǎng)h除另一個(gè)鏈表中所有大于200的值饲梭,可以將取舍值作為參數(shù)傳遞給tooBig()乘盖,但謂詞只能有一個(gè)參數(shù)。這時(shí)可以設(shè)計(jì)一個(gè)TooBig類排拷。
template<class T>
class TooBig
{
?? private:
????? T cutoff;
?? public:
????? TooBig(cosnt T & t) : cutoff(t){}
????? bool operator()(const T & v){return v > cutoff;}
};
72. transform有兩個(gè)版本。第一個(gè)版本接受4個(gè)參數(shù)锅尘,前兩個(gè)參數(shù)是指定容器區(qū)間的迭代器监氢,第三個(gè)參數(shù)是指定將結(jié)果復(fù)制到哪里的迭代器,最后一個(gè)參數(shù)是一個(gè)函數(shù)符藤违,它被用于區(qū)間的每個(gè)元素浪腐,生成結(jié)果中的新元素。
const int LIM = 5;
double arr1[LIM] = {36, 39, 42, 45, 48};
vector<double> gr8{arr1, arr1 + LIM};
ostream_iterator<double, char> out(cout, “ ”);
transform(gr8.begin(), gr8.end(), out, sqrt);
目標(biāo)迭代器可以位于原始區(qū)間中顿乒。例如议街,將out替換為gr8.begin(),新值將覆蓋原來的值璧榄。很明顯特漩,使用的函數(shù)符必須是接受單個(gè)參數(shù)的函數(shù)符。
第二種版本接受5個(gè)參數(shù)骨杂,第三個(gè)參數(shù)標(biāo)識(shí)第二個(gè)區(qū)間的起始位置涂身。
73. 頭文件functional定義了多個(gè)模板類函數(shù)對(duì)象。它們可以處理C++內(nèi)置類型或任何用戶自定義類型(如果重載了相應(yīng)的運(yùn)算符)搓蚪。P711表16.12蛤售。
74. P711自適應(yīng)函數(shù)符和函數(shù)適配器
表16.12預(yù)定義函數(shù)符都是自適應(yīng)的。STL有5個(gè)相關(guān)的概念:自適應(yīng)生成器妒潭、自適應(yīng)一元函數(shù)悴能、自適應(yīng)二元函數(shù)、自適應(yīng)謂詞和自適應(yīng)二元謂詞雳灾。STL使用binder1st和binder2nd將自適應(yīng)二元函數(shù)轉(zhuǎn)換為自適應(yīng)一元函數(shù)漠酿。binder1st將常數(shù)賦給第一個(gè)參數(shù),binder2nd將常數(shù)賦給第二個(gè)參數(shù)谎亩。
bind1st(multiplies<double>(), 2.5);
75. 對(duì)于算法函數(shù)設(shè)計(jì)记靡,有兩個(gè)通用部分。首先团驱,它們都用模板來提供泛型摸吠;其次,它們都使用迭代器來提供訪問容器中數(shù)據(jù)的通用表示嚎花。統(tǒng)一的容器設(shè)計(jì)使得不同類型的容器之間具有明顯關(guān)系寸痢。例如可以使用copy()將常規(guī)數(shù)組中的值復(fù)制到vector對(duì)象中,將vector對(duì)象中的值復(fù)制到list中紊选,將list對(duì)象中的值復(fù)制到set對(duì)象中啼止〉蓝海可以用==來比較兩個(gè)不同類型的容器,如deque和vector献烦。之所以能夠這樣做滓窍,是因?yàn)槿萜髦剌d的==運(yùn)算符使用迭代器來比較內(nèi)容,因此巩那,如果deque對(duì)象和vector對(duì)象的內(nèi)容相同吏夯,排列順序也相等,則它們是相等的即横。
76. STL將算法庫分為4種:
? 非修改式序列操作噪生,例如find(),for_each()
? 修改式序列操作东囚,例如transform()跺嗽,random_shuffle(),copy()
? 排序和相關(guān)操作页藻,sort()和其他各種操作桨嫁,包括集合操作
? 通用數(shù)字計(jì)算,包括將區(qū)間內(nèi)容累積份帐、計(jì)算兩個(gè)容器的內(nèi)部乘積等瞧甩。
前3組在頭文件algorithm中描述,第4組專用于數(shù)值數(shù)據(jù)的弥鹦,有自己的頭文件肚逸,稱為numeric。
77. 有些算法有兩個(gè)版本:就地版本和復(fù)制版本彬坏。STL的約定是朦促,復(fù)制版本的名稱以_copy結(jié)尾。復(fù)制版本將接受一個(gè)額外的輸出迭代器參數(shù)栓始,該參數(shù)指定輸出結(jié)果的放置位置务冕。
template <class ForwardIterator>
void replace(ForwardIterator first, ForwardIterator last, const T & old_value, const T & new_value);
它將所有的old_value替換為new_value。由于算法同時(shí)讀寫容器元素幻赚,因此迭代器類型必須是ForwardIterator或更高級(jí)別的禀忆。
復(fù)制版本的原型如下:
template<class InputIterator, class OuputIterator, class T>
OutputIterator replace_copy(InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T & new_value);
在這里,結(jié)果被復(fù)制到result指定的新位置落恼,因此對(duì)于指定區(qū)間而言箩退,只讀輸入迭代器足夠了。
注意佳谦,replace_copy的返回類型為OutputIterator戴涝。對(duì)于復(fù)制算法,統(tǒng)一的約定是,返回一個(gè)迭代器啥刻,該迭代器指向復(fù)制的最后一個(gè)值后面的一個(gè)位置奸鸯。
78. 另一個(gè)常見的變體是:有些函數(shù)有這樣的版本,即根據(jù)將函數(shù)應(yīng)用于容器元素得到的結(jié)果來執(zhí)行操作可帽,這些版本的名稱通常以_if結(jié)尾娄涩。例如,將函數(shù)用于舊值時(shí)映跟,如果返回的結(jié)果為true蓄拣,replace_if()將把舊值替換為新值。下面是該函數(shù)的原型:
template <class ForwardIterator, class Predicate, class T>
void replace_if(ForwardIterator first, ForwardIterator last, Predicate pred, const T & new_value);
還有一個(gè)replace_copy_if()版本申窘。
79. 與InputIterator一樣弯蚜,Predicate也是模板參數(shù)名稱孔轴,可以為T或U剃法。然而,STL使用Predicate來提醒用戶路鹰,實(shí)參應(yīng)該模擬Predicate概念贷洲。請(qǐng)記住,雖然文檔可以指出迭代器或函數(shù)符的概念晋柱,但編譯器不會(huì)對(duì)此執(zhí)行任何檢查优构。如果使用了錯(cuò)誤的迭代器,則編譯器試圖實(shí)例化模板時(shí)雁竞,將顯示大量的錯(cuò)誤信息钦椭。
80. STL和string
? string類雖然不是STL的組成部分,但設(shè)計(jì)它時(shí)考慮到了STL碑诉,可以使用STL接口彪腔。next_permutation將區(qū)間內(nèi)容轉(zhuǎn)換為下一種排列方式。對(duì)于字符串进栽,排列按照字母遞增的順序進(jìn)行德挣。如果成功,該算法返回true快毛,如果區(qū)間已經(jīng)處于最后的序列中格嗅,則該算法返回false。要得到區(qū)間的所有排列組合唠帝,因從最初的順序開始屯掖,為此程序使用了STL的sort()。
81. 有時(shí)可以選擇使用STL方法或STL函數(shù)襟衰。通常方法是更好的選擇懂扼。首先,它更適合于特定的容器;其次阀湿,作為成員函數(shù)赶熟,它可以使用模板類的內(nèi)存管理工具,從而在需要時(shí)調(diào)整容器的長度陷嘴。
82. STL設(shè)計(jì)者就是非常關(guān)心效率的算法人員映砖,算法是經(jīng)過仔細(xì)選擇的,而且是內(nèi)聯(lián)的灾挨。
83. vector邑退、valarray和array
? vector模板類是一個(gè)容器類和算法系統(tǒng)的一部分,它支持面向容器的操作劳澄,如排序地技、重新排列、搜索秒拔、將數(shù)據(jù)移到其它容器等等莫矗。而valarray類模板是面向數(shù)值計(jì)算的,不是STL的一部分砂缩。例如作谚,它沒有push_back()和insert()方法,但為很多數(shù)學(xué)運(yùn)算提供了一個(gè)簡(jiǎn)單庵芭、直觀的接口妹懒。最后,array是為替代內(nèi)置數(shù)組而設(shè)計(jì)的双吆,它通過提供更好眨唬、更安全的接口,讓數(shù)組更緊湊好乐,效率更高匾竿。Array表示長度固定的數(shù)組,因此不支持push_back()曹宴、insert()搂橙,但提供了多個(gè)STL方法,包括begin(),end(),rbegin(),rend()笛坦,這使得很容易將STL算法用于array對(duì)象区转。P720-P721valarray的用法。
valarray沒有begin()和end()方法版扩,C++11提供了接受valarray對(duì)象作為參數(shù)的模板函數(shù)begin()和end()废离。
valarray<doule> vad(10);
sort(begin(vad), end(vad));
84. 模板initializer_list? (C++11)
? std::vector<double> payments{45.99, 39.23, 19.95};
這之所以可行,是因?yàn)槿萜黝惉F(xiàn)在包含將initializer_list<T>作為參數(shù)的構(gòu)造函數(shù)礁芦,因此上述聲明相當(dāng)于:
std:vector<double> payments({45.99, 39.23, 19.95});
通瞅呔拢考慮到C++11新增的通用初始化語法悼尾,可使用表示法{}而不是()來調(diào)用類構(gòu)造函數(shù)。
shared_ptr<double> pd{new double};
但如果類有接受initializer_list作為參數(shù)的構(gòu)造函數(shù)肖方,則使用語法{}將調(diào)用該構(gòu)造函數(shù)闺魏。
std::vector<int> vi{10};
對(duì)應(yīng)的是std::vector<int> vi({10});
而不是std::vector<int> vi(10);
85. initializer_list
? 要在代碼中使用initializer_list對(duì)象,必須包含頭文件initiallizer_list俯画。這個(gè)模板類包含成員函數(shù)begin()和end()析桥,還包含成員函數(shù)size()。P726例子
initializer_list的迭代器類型為const艰垂,因此您不能通過其迭代器修改initiallizer_list中的值泡仗。
提供intializer_list的初衷旨在讓您能夠?qū)⒁幌盗兄祩鬟f給構(gòu)造函數(shù)或其它函數(shù)。