标准模板库(STL)学习指南之List链表
本文轉(zhuǎn)載自天極網(wǎng),原文地址:http://www.yesky.com/255/1910755.shtml.轉(zhuǎn)載請(qǐng)注明
什么是STL呢?STL就是Standard Template Library,標(biāo)準(zhǔn)模板庫(kù)。這可能是一個(gè)歷史上最令人興奮的工具的最無(wú)聊的術(shù)語(yǔ)。從根本上說(shuō),STL是一些“容器”的集合,這些“容器”有l(wèi)ist,vector,set,map等,STL也是算法和其他一些組件的集合。這里的“容器”和算法的集合指的是世界上很多聰明人很多年的杰作。
STL的目的是標(biāo)準(zhǔn)化組件,這樣就不用重新開(kāi)發(fā),可以使用現(xiàn)成的組件。STL現(xiàn)在是C++的一部分,因此不用額外安裝什麼。它被內(nèi)建在你的編譯器之內(nèi)。因?yàn)镾TL的list是一個(gè)簡(jiǎn)單的容器,所以我打算從它開(kāi)始介紹STL如何使用。如果你懂得了這個(gè)概念,其他的就都沒(méi)有問(wèn)題了。另外,list容器是相當(dāng)簡(jiǎn)單的,我們會(huì)看到這一點(diǎn)。
在本文中我們將會(huì)看到如何定義和初始化一個(gè)list,計(jì)算它的元素的數(shù)量,從一個(gè)list里查找元素,刪除元素,和一些其他的操作。要作到這些,我們將會(huì)討論兩個(gè)不同的算法,STL通用算法都是可以操作不止一個(gè)容器的,而list的成員函數(shù)是list容器專有的操作。
這是三類主要的STL組件的簡(jiǎn)明綱要。STL容器可以保存對(duì)象,內(nèi)建對(duì)象和類對(duì)象。它們會(huì)安全的保存對(duì)象,并定義我們能夠操作的這個(gè)對(duì)象的接口。放在蛋架上的雞蛋不會(huì)滾到桌上。它們很安全。因此,在STL容器中的對(duì)象也很安全。我知道這個(gè)比喻聽(tīng)起來(lái)很老土,但是它很正確。
STL算法是標(biāo)準(zhǔn)算法,我們可以把它們應(yīng)用在那些容器中的對(duì)象上。這些算法都有很著名的執(zhí)行特性。它們可以給對(duì)象排序,刪除它們,給它們記數(shù),比較,找出特殊的對(duì)象,把它們合并到另一個(gè)容器中,以及執(zhí)行其他有用的操作。
STL iterator就象是容器中指向?qū)ο蟮闹羔槨TL的算法使用iterator在容器上進(jìn)行操作。Iterator設(shè)置算法的邊界 ,容器的長(zhǎng)度,和其他一些事情。舉個(gè)例子,有些iterator僅讓算法讀元素,有一些讓算法寫(xiě)元素,有一些則兩者都行。 Iterator也決定在容器中處理的方向。
你可以通過(guò)調(diào)用容器的成員函數(shù)begin()來(lái)得到一個(gè)指向一個(gè)容器起始位置的iterator。你可以調(diào)用一個(gè)容器的 end() 函數(shù)來(lái)得到過(guò)去的最后一個(gè)值(就是處理停在那的那個(gè)值)。
這就是STL所有的東西,容器、算法、和允許算法工作在容器中的元素上的iterator。 算法以合適、標(biāo)準(zhǔn)的方法操作對(duì)象,并可通過(guò)iterator得到容器精確的長(zhǎng)度。一旦做了這些,它們就在也不會(huì)“跑出邊界”。 還有一些其他的對(duì)這些核心組件類型有功能性增強(qiáng)的組件,例如函數(shù)對(duì)象。我們將會(huì)看到有關(guān)這些的例子,現(xiàn)在 ,我們先來(lái)看一看STL的list。
定義一個(gè)list
我們可以象這樣來(lái)定義一個(gè)STL的list:
2 #include <list>
3 int main (void)
4 {
5 list<string> Milkshakes;
6 return 0;
7 }
這就行了,你已經(jīng)定義了一個(gè)list。簡(jiǎn)單嗎?list<string> Milkshakes這句是你聲明了list<string>模板類 的一個(gè)實(shí)例,然后就是實(shí)例化該類的一個(gè)對(duì)象。但是我們別急著做這個(gè)。在這一步其實(shí)你只需要知道你定義了 一個(gè)字符串的list。你需要包含提供STL list類的頭文件。我用gcc 2.7.2在我的Linux上編譯這個(gè)測(cè)試程序,例如:
g++ test1.cpp -o test1 注意iostream.h這個(gè)頭文件已經(jīng)被STL的頭文件放棄了。這就是為什么這個(gè)例子中沒(méi)有它的原因。
現(xiàn)在我們有了一個(gè)list,我們可以看實(shí)使用它來(lái)裝東西了。我們將把一個(gè)字符串加到這個(gè)list里。有一個(gè)非常 重要的東西叫做list的值類型。值類型就是list中的對(duì)象的類型。在這個(gè)例子中,這個(gè)list的值類型就是字符串,string , 這是因?yàn)檫@個(gè)list用來(lái)放字符串。
使用list的成員函數(shù)push_back和push_front插入一個(gè)元素到list中:
2 #include <list>
3 int main (void)
4 {
5 list<string> Milkshakes;
6 Milkshakes.push_back("Chocolate");
7 Milkshakes.push_back("Strawberry");
8 Milkshakes.push_front("Lime");
9 Milkshakes.push_front("Vanilla");
10 return 0;
11 }
我們現(xiàn)在有個(gè)4個(gè)字符串在list中。list的成員函數(shù)push_back()把一個(gè)對(duì)象放到一個(gè)list的后面,而 push_front()把對(duì)象放到前面。我通常把一些錯(cuò)誤信息push_back()到一個(gè)list中去,然后push_front()一個(gè)標(biāo)題到list中, 這樣它就會(huì)在這個(gè)錯(cuò)誤消息以前打印它了。
The list member function empty()list的成員函數(shù)empty()
知道一個(gè)list是否為空很重要。如果list為空,empty()這個(gè)成員函數(shù)返回真。 我通常會(huì)這樣使用它。通篇程序我都用push_back()來(lái)把錯(cuò)誤消息放到list中去。然后,通過(guò)調(diào)用empty() 我就可以說(shuō)出這個(gè)程序是否報(bào)告了錯(cuò)誤。如果我定義了一個(gè)list來(lái)放信息,一個(gè)放警告,一個(gè)放嚴(yán)重錯(cuò)誤, 我就可以通過(guò)使用empty()輕易的說(shuō)出到底有那種類型的錯(cuò)誤發(fā)生了。
我可以整理這些list,然后在打印它們之前,用標(biāo)題來(lái)整理它們,或者把它們排序成類。
2 || Using a list to track and report program messages and status
3 */
4 #include <iostream.h>
5 #include <string>
6 #include <list>
7 int main (void)
8 {
9 #define OK 0
10 #define INFO 1
11 #define WARNING 2
12 int return_code;
13 list<string> InfoMessages;
14 list<string> WarningMessages;
15 // during a program these messages are loaded at various points
16 InfoMessages.push_back("Info: Program started");
17 // do work...
18 WarningMessages.push_back("Warning: No Customer records have been found");
19 // do work...
20 return_code = OK;
21 if (!InfoMessages.empty()) {
22 // there were info messages
23 InfoMessages.push_front("Informational Messages:");
24 // ... print the info messages list, we'll see how later
25 return_code = INFO;
26 }
27 if (!WarningMessages.empty()) {
28 // there were warning messages
29 WarningMessages.push_front("Warning Messages:");
30 // ... print the warning messages list, we'll see how later
31 return_code = WARNING;
32 }
33 // If there were no messages say so.
34 if (InfoMessages.empty() && WarningMessages.empty()) {
35 cout << "There were no messages " << endl;
36 }
37 return return_code;
38 }
用for循環(huán)來(lái)處理list中的元素
我們想要遍歷一個(gè)list,比如打印一個(gè)中的所有對(duì)象來(lái)看看list上不同操作的結(jié)果。要一個(gè)元素一個(gè)元素的遍歷一個(gè)list, 我們可以這樣做:
2 || How to print the contents of a simple STL list. Whew!
3 */
4 #include <iostream.h>
5 #include <string>
6 #include <list>
7 int main (void)
8 {
9 list<string> Milkshakes;
10 list<string>::iterator MilkshakeIterator;
11 Milkshakes.push_back("Chocolate");
12 Milkshakes.push_back("Strawberry");
13 Milkshakes.push_front("Lime");
14 Milkshakes.push_front("Vanilla");
15 // print the milkshakes
16 Milkshakes.push_front("The Milkshake Menu");
17 Milkshakes.push_back("*** Thats the end ***");
18 for (MilkshakeIterator=Milkshakes.begin(); MilkshakeIterator!=Milkshakes.end(); ++MilkshakeIterator)
19 {
20 // dereference the iterator to get the element
21 cout << *MilkshakeIterator << endl;
22 }
23 }
這個(gè)程序定義了一個(gè)iterator,MilkshakeIterator。我們把它指向了這個(gè)list的第一個(gè)元素。 這可以調(diào)用Milkshakes.begin()來(lái)作到,它會(huì)返回一個(gè)指向list開(kāi)頭的iterator。然后我們把它和Milkshakes.end()的 返回值來(lái)做比較,當(dāng)我們到了那兒的時(shí)候就停下來(lái)。
容器的end()函數(shù)會(huì)返回一個(gè)指向容器的最后一個(gè)位置的iterator。當(dāng)我們到了那里,就停止操作。 我們不能不理容器的end()函數(shù)的返回值。我們僅知道它意味著已經(jīng)處理到了這個(gè)容器的末尾,應(yīng)該停止處理了。 所有的STL容器都要這樣做。
在上面的例子中,每一次執(zhí)行for循環(huán),我們就重復(fù)引用iterator來(lái)得到我們打印的字符串。
在STL編程中,我們?cè)诿總€(gè)算法中都使用一個(gè)或多個(gè)iterator。我們使用它們來(lái)存取容器中的對(duì)象。 要存取一個(gè)給定的對(duì)象,我們把一個(gè)iterator指向它,然后間接引用這個(gè)iterator。
這個(gè)list容器,就象你所想的,它不支持在iterator加一個(gè)數(shù)來(lái)指向隔一個(gè)的對(duì)象。 就是說(shuō),我們不能用Milkshakes.begin()+2來(lái)指向list中的第三個(gè)對(duì)象,因?yàn)镾TL的list是以雙鏈的list來(lái)實(shí)現(xiàn)的, 它不支持隨機(jī)存取。vector和deque(向量和雙端隊(duì)列)和一些其他的STL的容器可以支持隨機(jī)存取。
上面的程序打印出了list中的內(nèi)容。任何人讀了它都能馬上明白它是怎麼工作的。它使用標(biāo)準(zhǔn)的iterator和標(biāo)準(zhǔn) 的list容器。沒(méi)有多少程序員依賴它里面裝的東西, 僅僅是標(biāo)準(zhǔn)的C++。這是一個(gè)向前的重要步驟。這個(gè)例子使用STL使我們的軟件更加標(biāo)準(zhǔn)。
用STL的通用算法for_each來(lái)處理list中的元素
使用STL list和 iterator,我們要初始化、比較和給iterator增量來(lái)遍歷這個(gè)容器。STL通用的for_each 算法能夠減輕我們的工作。
2 || How to print a simple STL list MkII
3 */
4 #include <iostream.h>
5 #include <string>
6 #include <list>
7 #include <algorithm>
8 PrintIt (string& StringToPrint) {
9 cout << StringToPrint << endl;
10 }
11 int main (void) {
12 list<string> FruitAndVegetables;
13 FruitAndVegetables.push_back("carrot");
14 FruitAndVegetables.push_back("pumpkin");
15 FruitAndVegetables.push_back("potato");
16 FruitAndVegetables.push_front("apple");
17 FruitAndVegetables.push_front("pineapple");
18 for_each (FruitAndVegetables.begin(), FruitAndVegetables.end(), PrintIt);
19 }
在這個(gè)程序中我們使用STL的通用算法for_each()來(lái)遍歷一個(gè)iterator的范圍,然后調(diào)用PrintIt()來(lái)處理每個(gè)對(duì)象。 我們不需要初始化、比較和給iterator增量。for_each()為我們漂亮的完成了這些工作。我們執(zhí)行于對(duì)象上的 操作被很好的打包在這個(gè)函數(shù)以外了,我們不用再做那樣的循環(huán)了,我們的代碼更加清晰了。
for_each算法引用了iterator范圍的概念,這是一個(gè)由起始iterator和一個(gè)末尾iterator指出的范圍。 起始iterator指出操作由哪里開(kāi)始,末尾iterator指明到哪結(jié)束,但是它不包括在這個(gè)范圍內(nèi)。
用STL的通用算法count()來(lái)統(tǒng)計(jì)list中的元素個(gè)數(shù)
STL的通用算法count()和count_it()用來(lái)給容器中的對(duì)象記數(shù)。就象for_each()一樣,count()和count_if() 算法也是在iterator范圍內(nèi)來(lái)做的。
讓我們?cè)谝粋€(gè)學(xué)生測(cè)驗(yàn)成績(jī)的list中來(lái)數(shù)一數(shù)滿分的個(gè)數(shù)。這是一個(gè)整型的List。
2 || How to count objects in an STL list
3 */
4 #include <list>
5 #include <algorithm>
6 #
7 int main (void)
8 {
9 list<int> Scores;
10 #
11 Scores.push_back(100); Scores.push_back(80);
12 Scores.push_back(45); Scores.push_back(75);
13 Scores.push_back(99); Scores.push_back(100);
14 #
15 int NumberOf100Scores(0);
16 count (Scores.begin(), Scores.end(), 100, NumberOf100Scores);
17 #
18 cout << "There were " << NumberOf100Scores << " scores of 100" << endl;
19 }
count()算法統(tǒng)計(jì)等于某個(gè)值的對(duì)象的個(gè)數(shù)。上面的例子它檢查list中的每個(gè)整型對(duì)象是不是100。每次容器中的對(duì)象等于100,它就給NumberOf100Scores加1。這是程序的輸出:
There were 2 scores of 100用STL的通用算法count_if()來(lái)統(tǒng)計(jì)list中的元素個(gè)數(shù)
count_if()是count()的一個(gè)更有趣的版本。他采用了STL的一個(gè)新組件,函數(shù)對(duì)象。count_if() 帶一個(gè)函數(shù)對(duì)象的參數(shù)。函數(shù)對(duì)象是一個(gè)至少帶有一個(gè)operator()方法的類。有些STL算法作為參數(shù)接收 函數(shù)對(duì)象并調(diào)用這個(gè)函數(shù)對(duì)象的operator()方法。
函數(shù)對(duì)象被約定為STL算法調(diào)用operator時(shí)返回true或false。它們根據(jù)這個(gè)來(lái)判定這個(gè)函數(shù)。舉個(gè)例子會(huì) 說(shuō)的更清楚些。count_if()通過(guò)傳遞一個(gè)函數(shù)對(duì)象來(lái)作出比count()更加復(fù)雜的評(píng)估以確定一個(gè)對(duì)象是否應(yīng)該被 記數(shù)。在這個(gè)例子里我們將數(shù)一數(shù)牙刷的銷售數(shù)量。我們將提交包含四個(gè)字符的銷售碼和產(chǎn)品說(shuō)明的銷售記錄。
2 || Using a function object to help count things
3 */
4 #include <string>
5 #include <list>
6 #include <algorithm>
7 const string ToothbrushCode("0003");
8 class IsAToothbrush
9 {
10 public:
11 bool operator() ( string& SalesRecord )
12 {
13 return SalesRecord.substr(0,4)==ToothbrushCode;
14 }
15 };
16 int main (void)
17 {
18 list<string> SalesRecords;
19 SalesRecords.push_back("0001 Soap");
20 SalesRecords.push_back("0002 Shampoo");
21 SalesRecords.push_back("0003 Toothbrush");
22 SalesRecords.push_back("0004 Toothpaste");
23 SalesRecords.push_back("0003 Toothbrush");
24 int NumberOfToothbrushes(0);
25 count_if (SalesRecords.begin(), SalesRecords.end(),
26 IsAToothbrush(), NumberOfToothbrushes);
27 cout << "There were "
28 << NumberOfToothbrushes
29 << " toothbrushes sold" << endl;
30 }
這是這個(gè)程序的輸出:
There were 2 toothbrushes sold 這個(gè)程序是這樣工作的:定義一個(gè)函數(shù)對(duì)象類IsAToothbrush,這個(gè)類的對(duì)象能判斷出賣(mài)出的是否是牙刷 。如果這個(gè)記錄是賣(mài)出牙刷的記錄的話,函數(shù)調(diào)用operator()返回一個(gè)true,否則返回false。
count_if()算法由第一和第二兩個(gè)iterator參數(shù)指出的范圍來(lái)處理容器對(duì)象。它將對(duì)每個(gè) IsAToothbrush()返回true的容器中的對(duì)象增加NumberOfToothbrushes的值。
最后的結(jié)果是NumberOfToothbrushes這個(gè)變量保存了產(chǎn)品代碼域?yàn)?#34;0003"的記錄的個(gè)數(shù),也就是牙刷的個(gè)數(shù)。
注意count_if()的第三個(gè)參數(shù)IsAToothbrush(),它是由它的構(gòu)造函數(shù)臨時(shí)構(gòu)造的一個(gè)對(duì)象。你可以把IsAToothbrush類的一個(gè)臨時(shí)對(duì)象 傳遞給count_if()函數(shù)。count_if()將對(duì)該容器的每個(gè)對(duì)象調(diào)用這個(gè)函數(shù)。
使用count_if()的一個(gè)更加復(fù)雜的函數(shù)對(duì)象
我們可以更進(jìn)一步的研究一下函數(shù)對(duì)象。假設(shè)我們需要傳遞更多的信息給一個(gè)函數(shù)對(duì)象。我們不能通過(guò) 調(diào)用operator來(lái)作到這點(diǎn),因?yàn)楸仨毝x為一個(gè)list的中的對(duì)象的類型。 然而我們通過(guò)為IsAToothbrush指出一個(gè)非缺省的構(gòu)造函數(shù)就可以用任何我們所需要的信息來(lái)初始化它了。 例如,我們可能需要每個(gè)牙刷有一個(gè)不定的代碼。我們可以把這個(gè)信息加到下面的函數(shù)對(duì)象中:
2 || Using a more complex function object
3 */
4 #include <iostream.h>
5 #include <string>
6 #include <list>
7 #include <algorithm>
8 class IsAToothbrush
9 {
10 public:
11 IsAToothbrush(string& InToothbrushCode) :
12 ToothbrushCode(InToothbrushCode) {}
13 bool operator() (string& SalesRecord)
14 {
15 return SalesRecord.substr(0,4)==ToothbrushCode;
16 }
17 private:
18 string ToothbrushCode;
19 };
20 int main (void)
21 {
22 list<string> SalesRecords;
23 SalesRecords.push_back("0001 Soap");
24 SalesRecords.push_back("0002 Shampoo");
25 SalesRecords.push_back("0003 Toothbrush");
26 SalesRecords.push_back("0004 Toothpaste");
27 SalesRecords.push_back("0003 Toothbrush");
28 string VariableToothbrushCode("0003");
29 int NumberOfToothbrushes(0);
30 count_if (SalesRecords.begin(), SalesRecords.end(),
31 IsAToothbrush(VariableToothbrushCode),
32 NumberOfToothbrushes);
33 cout << "There were "
34 << NumberOfToothbrushes
35 << " toothbrushes matching code "
36 << VariableToothbrushCode
37 << " sold"
38 << endl;
39 }
程序的輸出是:
There were 2 toothbrushes matching code 0003 sold這個(gè)例子演示了如何向函數(shù)對(duì)象傳遞信息。你可以定義任意你想要的構(gòu)造函數(shù),你可以再函數(shù)對(duì)象中做任何你 想做的處理,都可以合法編譯通過(guò)。
你可以看到函數(shù)對(duì)象真的擴(kuò)展了基本記數(shù)算法。
到現(xiàn)在為止,我們都學(xué)習(xí)了:
·定義一個(gè)list
·向list中加入元素
·如何知道list是否為空
·如何使用for循環(huán)來(lái)遍歷一個(gè)list
·如何使用STL的通用算法for_each來(lái)遍歷list
·list成員函數(shù)begin() 和 end() 以及它們的意義
·iterator范圍的概念和一個(gè)范圍的最后一個(gè)位置實(shí)際上并不被處理這一事實(shí)
·如何使用STL通用算法count()和count_if()來(lái)對(duì)一個(gè)list中的對(duì)象記數(shù)
·如何定義一個(gè)函數(shù)對(duì)象
我選用這些例子來(lái)演示list的一般操作。如果你懂了這些基本原理,你就可以毫無(wú)疑問(wèn)的使用STL了 建議你作一些練習(xí)。我們現(xiàn)在用一些更加復(fù)雜的操作來(lái)擴(kuò)展我們的知識(shí),包括list成員函數(shù)和STL通用算法。
轉(zhuǎn)載于:https://www.cnblogs.com/ACShiryu/archive/2011/07/16/2108255.html
總結(jié)
以上是生活随笔為你收集整理的标准模板库(STL)学习指南之List链表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 为什么不能睡地板?
- 下一篇: oracle表数据误删恢复