程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> STL list鏈表的用法具體解析

STL list鏈表的用法具體解析

編輯:關於C++

STL list鏈表的用法具體解析。本站提示廣大學習愛好者:(STL list鏈表的用法具體解析)文章只能為提供參考,不一定能成為您想要的結果。以下是STL list鏈表的用法具體解析正文


本文以List容器為例子,引見了STL的根本內容,自在器到迭代器,再到通俗函數,並且例子豐碩,淺顯易懂。不掉為STL的入門文章,老手不容錯過!

0 媒介
1 界說一個list
2 應用list的成員函數push_back和push_front拔出一個元素到list中
3 list的成員函數empty()
4 用for輪回來處置list中的元素
5 用STL的通用算法for_each來處置list中的元素
6 用STL的通用算法count_if()來統計list中的元素個數
7 應用count_if()的一個加倍龐雜的函數對象。
8 應用STL通用算法find()在list中查找對象
9 應用STL通用算法find_if()在list中搜刮對象
10 應用STL通用算法search在list中找一個序列
11 應用list的成員函數sort()排序一個list。
12 用list的成員函數拔出元素到list中
13 List 結構函數
14 應用list成員函數從list中刪除元素
15 用list成員函數remove()從list中刪除元素。
16 應用STL通用算法remove()從list中刪除元素
17 應用STL通用算法stable_partition()和list成員函數splice()來劃分一個list
18 結論:在field中應用STL
19 參考書目

 
0 媒介
 
這篇文章是關於C++說話的一個新的擴大——尺度模板庫的(Standard Template Library),也叫STL。

當我第一次盤算寫一篇關於STL的文章的時刻,我不能不認可我其時低估了這個話題的深度和廣度。有許多內容要含蓋,也有許多具體描寫STL的書。是以我從新斟酌了一下我本來的設法主意。我為何要寫這篇文章,又為何要投稿呢?這會有什麽用呢?有再來一篇關於STL的文章的需要嗎?

當我掀開Musser and Saini的頁時,我看到了編程時期在我眼前融化。我能看到深夜宵掉了,目的軟件工程湧現了。我看到了可保護的代碼。一年曩昔了,我應用STL寫的軟件依然很輕易保護。讓人受驚的是其別人可以沒有我而保護的很好!

但是,我也記得在一開端的時刻很難弄懂那些技巧術語。一次,我買了Musser&Saini,每件事都順次湧現,然則在那之前我最盼望獲得的器械是一些好的例子。

當我開端的時刻,作為C++一部門的Stroustrup還沒出來,它籠罩了STL。
是以我想寫一篇關於一個STL法式員的真實生涯的文章能夠會有效。假如我手上有一些好的例子的話,特殊是象如許的新標題,我會學的更快。

別的一件事是STL應當很好用。是以,實際上說,我們應當可以立時開端應用STL。
什麽是STL呢?STL就是Standard Template Library,尺度模板庫。這能夠是一個汗青上最使人高興的對象的最無聊的術語。從基本上說,STL是一些“容器”的聚集,這些“容器”有list,vector,set,map等,STL也是算法和其他一些組件的聚集。這裡的“容器”和算法的聚集指的是世界上許多聰慧人許多年的佳構。

STL的目標是尺度化組件,如許你就不消從新開辟它們了。你可以僅僅應用這些現成的組件。STL如今是C++的一部門,是以不消額定裝置什麽。它被內建在你的編譯器以內。由於STL的list是一個簡略的容器,所以我盤算從它開端引見STL若何應用。假如你理解了這個概念,其他的就都沒有成績了。別的,list容器是相當簡略的,我們會看到這一點。

這篇文章中我們將會看到若何界說和初始化一個list,盤算它的元素的數目,從一個list裡查找元素,刪除元素,和一些其他的操作。要作到這些,我們將會評論辯論兩個分歧的算法,STL通用算法都是可以操作不止一個容器的,而list的成員函數是list容器專有的操作。

這是三類重要的STL組件的簡明綱領。STL容器可以保留對象,內建對象和類對象。它們會平安的保留對象,並界說我們可以或許操作的這個對象的接口。放在蛋架上的雞蛋不會滾到桌上。它們很平安。是以,在STL容器中的對象也很平安。我曉得這個比方聽起來很老土,然則它很准確。

STL算法是尺度算法,我們可以把它們運用在那些容器中的對象上。這些算法都有很有名的履行特征。它們可以給對象排序,刪除它們,給它們記數,比擬,找出特別的對象,把它們歸並到另外一個容器中,和履行其他有效的操作。

STL iterator就象是容器中指向對象的指針。STL的算法應用iterator在容器長進行操作。Iterator設置算法的界限,容器的長度,和其他一些工作。舉個例子,有些iterator僅讓算法讀元素,有一些讓算法寫元素,有一些則二者都行。 Iterator也決議在容器中處置的偏向。

你可以經由過程挪用容器的成員函數begin()來獲得一個指向一個容器肇端地位的iterator。你可以挪用一個容器的 end() 函數來獲得曩昔的最初一個值(就是處置停在那的誰人值)。

這就是STL一切的器械,容器、算法、和許可算法任務在容器中的元素上的iterator。算法以適合、尺度的辦法操尴尬刁難象,並可經由過程iterator獲得容器准確的長度。一旦做了這些,它們就在也不會“跑出界限”。還有一些其他的對這些焦點組件類型有功效性加強的組件,例如函數對象。我們將會看到有關這些的例子,如今 ,我們先來看一看STL的list。

 
-----------------------------------------------------------

1 界說一個list
 
我們可以象如許來界說一個STL的list:
#include <string>
#include <list>
int main (void)
{
list<string> Milkshakes;
return 0;
}
這就好了,你曾經界說了一個list。簡略嗎?list<string> Milkshakes這句是你聲清楚明了list<string>模板類的一個實例,然後就是實例化這個類的一個對象。然則我們別急著做這個。在這一步其實你只須要曉得你界說了 一個字符串的list。你須要包括供給STL list類的頭文件。我用gcc 2.7.2在我的Linux上編譯這個測試法式,例如:

g++ test1.cpp -o test1

留意iostream.h這個頭文件曾經被STL的頭文件廢棄了。這就是為何這個例子中沒有它的緣由。

如今我們有了一個list,我們可以看實應用它來裝器械了。我們將把一個字符串加到這個list裡。有一個異常 主要的器械叫做list的值類型。值類型就是list中的對象的類型。在這個例子中,這個list的值類型就是字符串,string , 這是由於這個list用來放字符串。

 
----------------------------------------------------------

2 應用list的成員函數push_back和push_front拔出一個元素到list中

#include <string>
#include <list>

int main (void)
{
list<string> Milkshakes;
Milkshakes.push_back("Chocolate");
Milkshakes.push_back("Strawberry");
Milkshakes.push_front("Lime");
Milkshakes.push_front("Vanilla");
return 0;
}

我們如今有個4個字符串在list中。list的成員函數push_back()把一個對象放到一個list的前面,而 push_front()把對象放到後面。我平日把一些毛病信息push_back()到一個list中去,然後push_front()一個題目到list中, 如許它就會在這個毛病新聞之前打印它了。

---------------------------------------------------------------

3 list的成員函數empty()
 
曉得一個list能否為空很主要。假如list為空,empty()這個成員函數前往真。 我平日會如許應用它。通篇法式我都用push_back()來把毛病新聞放到list中去。然後,經由過程挪用empty() 我便可以說出這個法式能否申報了毛病。假如我界說了一個list來放信息,一個放正告,一個放嚴重毛病, 我便可以經由過程應用empty()隨意馬虎的說出究竟有那品種型的毛病產生了。

我可以整頓這些list,然後在打印它們之前,用題目來整頓它們,或許把它們排序成類。

/*
|| Using a list to track and report program messages and status
*/
#include <iostream.h>
#include <string>
#include <list>
int main (void)
{
#define OK 0
#define INFO 1
#define WARNING 2
int return_code;
list<string> InfoMessages;
list<string> WarningMessages;

// during a program these messages are loaded at various points
InfoMessages.push_back("Info: Program started");
// do work...
WarningMessages.push_back("Warning: No Customer records have been found");
// do work...

  return_code = OK;

if  (!InfoMessages.empty()) {  // there were info messages
   InfoMessages.push_front("Informational Messages:");
   // ... print the info messages list, we'll see how later
   return_code = INFO;
}

if  (!WarningMessages.empty()) {   // there were warning messages
   WarningMessages.push_front("Warning Messages:");
   // ... print the warning messages list, we'll see how later
   return_code = WARNING;
}

// If there were no messages say so.
if (InfoMessages.empty() && WarningMessages.empty()) {
   cout << "There were no messages " << endl;
}

return return_code;
}

4 用for輪回來處置list中的元素
 
我們想要遍歷一個list,好比打印一個中的一切對象來看看list上分歧操作的成果。要一個元素一個元素的遍歷一個list, 我們可以如許做:

/*
|| How to print the contents of a simple STL list. Whew!
*/
#include <iostream.h>
#include <string>
#include <list>

int main (void)
{
list<string> Milkshakes;
list<string>::iterator MilkshakeIterator;

Milkshakes.push_back("Chocolate");
Milkshakes.push_back("Strawberry");
Milkshakes.push_front("Lime");
Milkshakes.push_front("Vanilla");

// print the milkshakes
Milkshakes.push_front("The Milkshake Menu");
Milkshakes.push_back("*** Thats the end ***");
for (MilkshakeIterator=Milkshakes.begin();
   MilkshakeIterator!=Milkshakes.end();
++MilkshakeIterator)
{
  // dereference the iterator to get the element
  cout << *MilkshakeIterator << endl;
}
}

這個法式界說了一個iterator,MilkshakeIterator。我們把它指向了這個list的第一個元素。 這可以挪用Milkshakes.begin()來作到,它會前往一個指向list開首的iterator。然後我們把它和Milkshakes.end()的 前往值來做比擬,當我們到了那兒的時刻就停上去。

容器的end()函數會前往一個指向容器的最初一個地位的iterator。當我們到了那邊,就停滯操作。 我們不克不及不睬容器的end()函數的前往值。我們僅曉得它意味著曾經處置到了這個容器的末尾,應當停滯處置了。 一切的STL容器都要如許做。

在下面的例子中,每次履行for輪回,我們就反復援用iterator來獲得我們打印的字符串。

在STL編程中,我們在每一個算法中都應用一個或多個iterator。我們應用它們來存取容器中的對象。 要存取一個給定的對象,我們把一個iterator指向它,然後直接援用這個iterator。

這個list容器,就象你所想的,它不支撐在iterator加一個數來指向隔一個的對象。 就是說,我們不克不及用Milkshakes.begin()+2來指向list中的第三個對象,由於STL的list是以雙鏈的list來完成的, 它不支撐隨機存取。vector和deque(向量和雙端隊列)和一些其他的STL的容器可以支撐隨機存取。

下面的法式打印出了list中的內容。任何人讀了它都能立時明確它是怎麽任務的。它應用尺度的iterator和尺度 的list容器。沒有若干法式員依附它外面裝的器械, 僅僅是尺度的C++。這是一個向前的主要步調。這個例子應用STL使我們的軟件加倍尺度。

------------------------------------------------------------

5 用STL的通用算法for_each來處置list中的元素
 
應用STL list和 iterator,我們要初始化、比擬和給iterator增量來遍歷這個容器。STL通用的for_each 算法可以或許加重我們的任務。

/*
|| How to print a simple STL list MkII
*/
#include <iostream.h>
#include <string>
#include <list>
#include <algorithm>

PrintIt (string& StringToPrint)
{
cout << StringToPrint << endl;
}

int main (void)
{
list<string> FruitAndVegetables;
FruitAndVegetables.push_back("carrot");
FruitAndVegetables.push_back("pumpkin");
FruitAndVegetables.push_back("potato");
FruitAndVegetables.push_front("apple");
FruitAndVegetables.push_front("pineapple");

for_each  (FruitAndVegetables.begin(), FruitAndVegetables.end(), PrintIt);
}

在這個法式中我們應用STL的通用算法for_each()來遍歷一個iterator的規模,然後挪用PrintIt()來處置每一個對象。 我們不須要初始化、比擬和給iterator增量。for_each()為我們英俊的完成了這些任務。我們履行於對象上的 操作被很好的打包在這個函數之外了,我們不消再做那樣的輪回了,我們的代碼加倍清楚了。

for_each算法援用了iterator規模的概念,這是一個由肇端iterator和一個末尾iterator指出的規模。 肇端iterator指出操作由哪裡開端,末尾iterator指明到哪停止,然則它不包含在這個規模內。用STL的通用算法count()來統計list中的元素個數。

--------------------------------------------------------------------------------

5.2用STL的通用算法count()來統計list中的元素個數
 
STL的通用算法count()和count_if()用來給容器中的對象記數。就象for_each()一樣,count()和count_if() 算法也是在iterator規模內來做的。

讓我們在一個先生考試成就的list中來數一數滿分的個數。這是一個整型的List。

/*
|| How to count objects in an STL list
*/
#include <list>
#include <algorithm>
int main (void)
{
list<int> Scores;
Scores.push_back(100);
Scores.push_back(80);
Scores.push_back(45);
Scores.push_back(75);
Scores.push_back(99);
Scores.push_back(100);

int NumberOf100Scores(0);
//count (Scores.begin(), Scores.end(), 100, NumberOf100Scores);
NumberOf100Scores = count(Scores.begin(), Scores.end(), 100);
cout << "There were " << NumberOf100Scores << " scores of 100" << endl;
}

count()算法統計等於某個值的對象的個數。下面的例子它檢討list中的每一個整型對象是否是100。每次容器中的對象等於100,它就給NumberOf100Scores加1。這是法式的輸入:

法式的輸入:
There were 2 scores of 100

--------------------------------------------------------------------------------

6.用STL的通用算法count_if()來統計list中的元素個數
 
count_if()是count()的一個更風趣的版本。他采取了STL的一個新組件,函數對象。count_if() 帶一個函數對象的參數。函數對象是一個至多帶有一個operator()辦法的類。有些STL算法作為參數吸收函數對象並挪用這個函數對象的operator()辦法。
函數對象被商定為STL算法挪用operator時前往true或false。它們依據這個來剖斷這個函數。舉個例子會說的更清晰些。
count_if()經由過程傳遞一個函數對象來作出比count()加倍龐雜的評價以肯定一個對象能否應當被記數。

在這個例子裡我們將數一數牙刷的發賣數目。我們將提交包括四個字符的發賣碼和產物解釋的發賣記載。

/* || Using a function object to help count things */
#include <string>
#include <list>
#include <algorithm>

const string ToothbrushCode("0003");

class IsAToothbrush
{
public:
bool operator() ( string& SalesRecord )
{
return SalesRecord.substr(0,4)==ToothbrushCode;
}
};

int main (void)
{
list<string> SalesRecords;
SalesRecords.push_back("0001 Soap");
SalesRecords.push_back("0002 Shampoo");
SalesRecords.push_back("0003 Toothbrush");
SalesRecords.push_back("0004 Toothpaste");
SalesRecords.push_back("0003 Toothbrush");
int NumberOfToothbrushes(0);
count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush(), NumberOfToothbrushes);
cout << "There were " << NumberOfToothbrushes << " toothbrushes sold" << endl;
}

這是這個法式的輸入:
There were 2 toothbrushes sold

這個法式是如許任務的:界說一個函數對象類IsAToothbrush,這個類的對象能斷定出賣出的能否是牙刷 。假如這個記載是賣出牙刷的記載的話,函數挪用operator()前往一個true,不然前往false。

count_if()算法由第一和第二兩個iterator參數指出的規模來處置容器對象。它將對每一個 IsAToothbrush()前往true的容器中的對象增長NumberOfToothbrushes的值。

最初的成果是NumberOfToothbrushes這個變量保留了產物代碼域為"0003"的記載的個數,也就是牙刷的個數。

留意count_if()的第三個參數IsAToothbrush(),它是由它的結構函數暫時結構的一個對象。你可以把IsAToothbrush類的一個暫時對象 傳遞給count_if()函數。count_if()將對該容器的每一個對象挪用這個函數。

--------------------------------------------------------------------------------

7 應用count_if()的一個加倍龐雜的函數對象。
 
應用count_if()的一個加倍龐雜的函數對象。
我們可以更進一步的研討一下函數對象。假定我們須要傳遞更多的信息給一個函數對象。我們不克不及經由過程挪用operator來作到這點,由於必需界說為一個list的中的對象的類型。 但是我們經由過程為IsAToothbrush指出一個非缺省的結構函數便可以用任何我們所須要的信息來初始化它了。 例如,我們能夠須要每一個牙刷有一個不定的代碼。我們可以把這個信息加到上面的函數對象中:

/*
|| Using a more complex function object
*/
#include <iostream.h>
#include <string>
#include <list>
#include <algorithm>

class IsAToothbrush
{
public:
IsAToothbrush(string& InToothbrushCode) : ToothbrushCode(InToothbrushCode) {}
bool operator() (string& SalesRecord)
{
return SalesRecord.substr(0,4)==ToothbrushCode;
}
private:
string ToothbrushCode;
};

int main (void)
{
list<string> SalesRecords;

SalesRecords.push_back("0001 Soap");
SalesRecords.push_back("0002 Shampoo");
SalesRecords.push_back("0003 Toothbrush");
SalesRecords.push_back("0004 Toothpaste");
SalesRecords.push_back("0003 Toothbrush");

string VariableToothbrushCode("0003");

int NumberOfToothbrushes(0);
count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush(VariableToothbrushCode), NumberOfToothbrushes);
cout << "There were  "
 << NumberOfToothbrushes
 << " toothbrushes matching code "
 << VariableToothbrushCode
 << " sold"
 << endl;
}

法式的輸入是:
There were 2 toothbrushes matching code 0003 sold

這個例子演示了若何向函數對象傳遞信息。你可以界說隨意率性你想要的結構函數,你可以再函數對象中做任何你 想做的處置,都可以正當編譯經由過程。

你可以看到函數對象真的擴大了根本記數算法。

到如今為止,我們都進修了:

界說一個list
向list中參加元素
若何曉得list能否為空
若何應用for輪回來遍歷一個list
若何應用STL的通用算法for_each來遍歷list
list成員函數begin() 和 end() 和它們的意義
iterator規模的概念和一個規模的最初一個地位現實上其實不被處置這一現實
若何應用STL通用算法count()和count_if()來對一個list中的對象記數
若何界說一個函數對象
我選用這些例子來演示list的普通操作。假如你懂了這些根本道理,你便可以毫無疑問的應用STL了 建議你作一些演習。我們如今用一些加倍龐雜的操作來擴大我們的常識,包含list成員函數和STL通用算法。

輸入是:
Pineapple

假如沒有找到指出的對象,就會前往Fruit.end()的值,如果找到了就前往一個指著找到的對象的iterator

--------------------------------------------------------

8.應用STL通用算法find()在list中查找對象
 
我們若何在list中查找器械呢?
STL的通用算法find()和find_if()可以做這些。
就象for_each(), count(), count_if() 一樣,這些算法也應用iterator規模,這個規模指出一個list或隨意率性其他容器中的一部門來處置。平日首iterator指著開端的地位,次iterator指著停滯處置的處所。由次iterator指出的元素不被處置。
這是find()若何任務:

/*
|| How to find things in an STL list
*/
#include <string>
#include <list>
#include <algorithm>

int main (void)
{
list<string> Fruit;
list<string>::iterator FruitIterator;

Fruit.push_back("Apple");
Fruit.push_back("Pineapple");
Fruit.push_back("Star Apple");

FruitIterator = find (Fruit.begin(), Fruit.end(), "Pineapple");

if (FruitIterator == Fruit.end())
{
cout << "Fruit not found in list" << endl;
}
else
{
cout << *FruitIterator << endl;
}
}

輸入是:
Pineapple
假如沒有找到指出的對象,就會前往Fruit.end()的值,如果找到了就前往一個指著找到的對象i

--------------------------------------------------

9.應用STL通用算法find_if()在list中搜刮對象
 
這是find()的一個更壯大的版本。這個例子演示了find_if(),它吸收一個函數對象的參數作為參數,並應用它來做更龐雜的評價對象能否和給出的查找前提相付。
假定我們的list中有一些按年月分列的包括了事宜和日期的記載。我們願望找動身生在1997年的事宜。

這是find()的一個更壯大的版本。這個例子演示了find_if(),它吸收一個函數對象的參數作為參數, 並應用它來做更龐雜的評價對象能否和給出的查找前提相付。假定我們的list中有一些按年月分列的包括了事宜和日期的記載。我們願望找動身生在1997年的事宜。

/*
|| How to find things in an STL list MkII
*/
#include <string>
#include <list>
#include <algorithm>

class EventIsIn1997
{
public:
bool operator () (string& EventRecord)
{
// year field is at position 12 for 4 characters in EventRecord
return EventRecord.substr(12,4)=="1997";
}
};

int main (void)
{
list<string> Events;

// string positions 0123456789012345678901234567890123456789012345
Events.push_back("07 January 1995 Draft plan of house prepared");
Events.push_back("07 February 1996 Detailed plan of house prepared");
Events.push_back("10 January 1997 Client agrees to job");
Events.push_back("15 January 1997 Builder starts work on bedroom");
Events.push_back("30 April 1997 Builder finishes work");

list<string>::iterator EventIterator = find_if (Events.begin(), Events.end(), EventIsIn1997());

// find_if completes the first time EventIsIn1997()() returns true
// for any object. It returns an iterator to that object which we
// can dereference to get the object, or if EventIsIn1997()() never
// returned true, find_if returns end()
if (EventIterator==Events.end())
{
cout << "Event not found in list" << endl;
}
else
{
cout << *EventIterator << endl;
}
}

這是法式的輸入:
10 January 1997 Client agrees to job

-----------------------------------------------------

10.應用STL通用算法search在list中找一個序列
 
一些字符在STL容器中很利益理,讓我們看一看一個難處置的字符序列。我們將界說一個list來放字符。
list<char> Characters;

如今我們有了一個字符序列,它不消任何贊助就曉得然後治理內存。它曉得它是從哪裡開端、到哪裡停止。 它異常有效。我不曉得我能否說過以null開頭的字符數組。

讓我們參加一些我們愛好的字符到這個list中:

Characters.push_back('\0');
Characters.push_back('\0');
Characters.push_back('1');
Characters.push_back('2');

我們將獲得若干個空字符呢?
int NumberOfNullCharacters(0);
count(Characters.begin(), Characters.end(), '\0', NumberOfNullCharacters);
cout << "We have " << NumberOfNullCharacters << endl;

讓我們找字符'1'
list<char>::iterator Iter;
Iter = find(Characters.begin(), Characters.end(), '1');
cout << "We found " << *Iter << endl;

這個例子演示了STL容器許可你以更尺度的辦法來處置空字符。如今讓我們用STL的search算法來搜刮容器中 的兩個null。就象你猜的一樣,STL通用算法search()用來搜刮一個容器,然則是搜刮一個元素串,不象find()和find_if() 只搜刮單個的元素。

/*
|| How to use the search algorithm in an STL list
*/
#include <string>
#include <list>
#include <algorithm>

int main ( void)
{
list<char> TargetCharacters;
list<char> ListOfCharacters;

TargetCharacters.push_back('\0');
TargetCharacters.push_back('\0');

ListOfCharacters.push_back('1');
ListOfCharacters.push_back('2');
ListOfCharacters.push_back('\0');
ListOfCharacters.push_back('\0');

list<char>::iterator PositionOfNulls = search(ListOfCharacters.begin(), ListOfCharacters.end(), TargetCharacters.begin(), TargetCharacters.end());

if (PositionOfNulls!=ListOfCharacters.end())
cout << "We found the nulls" << endl;
}
The output of the program will be 這是法式的輸入:
We found the nulls

search算法在一個序列中找另外一個序列的第一次湧現的地位。在這個例子裡我們在ListOfCharacters中 找TargetCharacters這個序列的第一次湧現,TargetCharacters是包括兩個null字符的序列。 search的參數是兩個指著查找目的的iterator和兩個指著搜刮規模的iterators。 是以我們我們在全部的ListOfCharacters的規模外調找TargetCharacters這個list的全部序列。

假如TargetCharacters被發明,search就會前往一個指著ListOfCharacters中序列婚配的第一個 字符的iterator。假如沒有找到婚配項,search前往ListOfCharacters.end()的值。

--------------------------------------------------------------------------------

11 應用list的成員函數sort()排序一個list。

要排序一個list,我們要用list的成員函數sort(),而不是通用算法sort()。一切我們用過的算法都是通用算法。但是,在STL中有時容器支撐它本身對一個特別算法的完成,這平日是為了進步機能。

在這個例子中,list容器有它本身的sort算法,這是由於通用算法僅能為那些供給隨機存取外面元素的容器排序,而因為list是作為一個銜接的鏈表完成的,它不支撐對它外面的元素隨機存取。所以就須要一個特別的 sort()成員函數來排序list。

因為各類緣由,容器在機能須要較高或有特別後果需求的場所支撐內部函數(extra functions), 這經由過程應用結構函數的構造特征可以作到。

/* || How to sort an STL list */
#include <string>
#include <list>
#include <algorithm>
PrintIt (string& StringToPrint)
{
cout << StringToPrint << endl;
}

int main (void)
{
list<string> Staff;
list<string>::iterator PeopleIterator;
Staff.push_back("John");
Staff.push_back("Bill");
Staff.push_back("Tony");
Staff.push_back("Fidel");
Staff.push_back("Nelson");
cout << "The unsorted list " << endl;
for_each(Staff.begin(), Staff.end(), PrintIt );
Staff.sort();
cout << "The sorted list " << endl;
for_each(Staff.begin(), Staff.end(), PrintIt);
}

輸入是:
The unsorted list  John Bill Tony Fidel Nelson The sorted list  Bill Fidel John Nelson Tony

--------------------------------------------------------------------------------

12.用list的成員函數拔出元素到list中
 
list的成員函數push_front()和push_back()分離把元素參加到list的後面和前面。你可使用insert() 把對象拔出到list中的任何處所。

insert()可以參加一個對象,一個對象的若干份拷貝,或許一個規模之內的對象。這裡是一些拔出對象到list中的例子:

/*
|| Using insert to insert elements into a list.
*/
#include <list>

int main (void)
{
list<int> list1;

/*
|| Put integers 0 to 9 in the list
*/
for (int i = 0; i < 10; ++i) list1.push_back(i);

/*
|| Insert -1 using the insert member function
|| Our list will contain -1,0,1,2,3,4,5,6,7,8,9
*/
list1.insert(list1.begin(), -1);

/*
|| Insert an element at the end using insert
|| Our list will contain -1,0,1,2,3,4,5,6,7,8,9,10
*/
list1.insert(list1.end(), 10);

/*
|| Inserting a range from another container
|| Our list will contain -1,0,1,2,3,4,5,6,7,8,9,10,11,12
*/
int IntArray[2] = {11,12};
list1.insert(list1.end(), &IntArray[0], &IntArray[2]);

/*
|| As an exercise put the code in here to print the lists!
|| Hint: use PrintIt and accept an interger
*/
}

留意,insert()函數把一個或若干個元素拔出到你指出的iterator的地位。你的元素將湧現在 iterator指出的地位之前。

-------------------------------------------------------

13.List 結構函數
 
我們曾經象如許界說了list:
list<int> Fred;

你也能夠象如許界說一個list,並同時初始化它的元素:
// define a list of 10 elements and initialise them all to 0
list<int> Fred(10, 0);
// list now contains 0,0,0,0,0,0,0,0,0,0

或許你可以界說一個list並用另外一個STL容器的一個規模來初始化它,這個STL容器紛歧定是一個list, 僅僅須要是元素類型雷同的的容器便可以。
vector<int> Harry;
Harry.push_back(1);
Harry.push_back(2);
#
// define a list and initialise it with the elements in Harry
list<int> Bill(Harry.begin(), Harry.end());
// Bill now contains 1,2

--------------------------------------------------------------------------------

14.應用list成員函數從list中刪除元素
 
list成員函數pop_front()刪失落list中的第一個元素,pop_back()刪失落最初一個元素。函數erase()刪失落由一個iterator指出的元素。還有另外一個erase()函數可以刪失落一個規模的元素。

/* || Erasing objects from a list */
#include <list>
int main (void)
{
list<int> list1;// define a list of integers

/* || Put some numbers in the list || It now contains 0,1,2,3,4,5,6,7,8,9 */
for (int i = 0; i < 10; ++i)
list1.push_back(i);

list1.pop_front();// erase the first element 0

list1.pop_back(); // erase the last element 9

list1.erase(list1.begin());
// erase the first element (1) using an iterator

list1.erase(list1.begin(), list1.end());
// erase all the remaining elements

cout << "list contains " << list1.size() << " elements" << endl;
}

輸入是:
list contains 0 elements

-----------------------------------------------------

15.用list成員函數remove()從list中刪除元素。

/* || Using the list member function remove to remove elements */
#include <string>
#include <list>
#include <algorithm>
PrintIt (const string& StringToPrint)
{
cout << StringToPrint << endl;
}

int main (void)
{
list<string> Birds;
Birds.push_back("cockatoo");
Birds.push_back("galah");
Birds.push_back("cockatoo");
Birds.push_back("rosella");
Birds.push_back("corella");
cout << "Original list with cockatoos" << endl;
for_each(Birds.begin(), Birds.end(), PrintIt);
Birds.remove("cockatoo");
cout << "Now no cockatoos" << endl;
for_each(Birds.begin(), Birds.end(), PrintIt);
}

輸入是:
Original list with cockatoos cockatoo galah cockatoo rosella corella Now no cockatoos galah rosella corella

------------------------------------------------------------

16.應用STL通用算法remove()從list中刪除元素
 
通用算法remove()應用和list的成員函數分歧的方法任務。普通情形下不轉變容器的年夜小。

/*
|| Using the generic remove algorithm to remove list elements
*/
#include <string>
#include <list>
#include <algorithm>

PrintIt(string& AString) { cout << AString << endl; }

int main (void)
{
list<string> Birds;
list<string>::iterator NewEnd;

Birds.push_back("cockatoo");
Birds.push_back("galah");
Birds.push_back("cockatoo");
Birds.push_back("rosella");
Birds.push_back("king parrot");

cout << "Original list" << endl;
for_each(Birds.begin(), Birds.end(), PrintIt);

NewEnd = remove(Birds.begin(), Birds.end(), "cockatoo");

cout << endl << "List according to new past the end iterator" << endl;
for_each(Birds.begin(), NewEnd, PrintIt);

cout << endl << "Original list now. Care required!" << endl;
for_each(Birds.begin(), Birds.end(), PrintIt);
}

輸入成果將為:
Original list
cockatoo
galah
cockatoo
rosella
king parrot

List according to new past the end iterator
galah
rosella
king parrot

Original list now. Care required!
galah
rosella
king parrot
rosella
king parrot
通用remove()算法前往一個指向新的list的開頭的iterator。從開端到這個新的開頭(不含新開頭元素)的規模 包括了remove後剩下一切元素。你可以用list成員函數erase函數來刪除重新開頭到老開頭的部門。

-----------------------------------------------------

17.應用STL通用算法stable_partition()和list成員函數splice()來劃分一個list
 
應用STL通用算法stable_partition()和list成員函數splice()來劃分一個list
我們將完成一個略微有點龐雜的例子。它演示STL通用算法stable_partition()算法和一個list成員函數 splice()的變更。留意函數對象的應用和沒有應用輪回。 經由過程簡略的語句挪用STL算法來掌握。
stable_partition()是一個風趣的函數。它從新分列元素,使得知足指定前提的元素排在 不知足前提的元素後面。它保持著兩組元素的次序關系。

splice 把另外一個list中的元素聯合到一個list中。它從源list中刪除元素。

在這個例子中,我們想從敕令行吸收一些標記和四個文件名。文件名必需'按次序湧現。經由過程應用stable_partition() 我們可以吸收和文件名混為任何地位的標記,而且不打亂文件名的次序就把它們放到一路。

因為記數和查找算法都很易用,我們挪用這些算法來決議哪一個標記被設置而哪一個標記未被設置。 我發明容器用來治理大批的象如許的靜態數據。

/*
|| Using the STL stable_partition algorithm
|| Takes any number of flags on the command line and
|| four filenames in order.
*/
#include <string>
#include <list>
#include <algorithm>

PrintIt ( string& AString { cout << AString << endl; }

class IsAFlag {
public:
bool operator () (string& PossibleFlag) {
return PossibleFlag.substr(0,1)=="-";
}
};

class IsAFileName {
public:
bool operator () (string& StringToCheck) {
return !IsAFlag()(StringToCheck);
}
};

class IsHelpFlag {
public:
bool operator () (string& PossibleHelpFlag) {
return PossibleHelpFlag=="-h";
}
};

int main (int argc, char *argv[]) {

list<string> CmdLineParameters; // the command line parameters
list<string>::iterator StartOfFiles; // start of filenames
list<string> Flags; // list of flags
list<string> FileNames; // list of filenames

for (int i = 0; i < argc; ++i)
CmdLineParameters.push_back(argv[i]);

CmdLineParameters.pop_front(); // we don't want the program name

// make sure we have the four mandatory file names
int NumberOfFiles(0);
count_if(CmdLineParameters.begin(), CmdLineParameters.end(), IsAFileName(), NumberOfFiles);

cout << "The " << (NumberOfFiles == 4 ? "correct " : "wrong ") << "number (" << NumberOfFiles << ") of file names were specified" << endl;

 // move any flags to the beginning
StartOfFiles = stable_partition(CmdLineParameters.begin(), CmdLineParameters.end(), IsAFlag());

cout << "Command line parameters after stable partition" << endl;
for_each(CmdLineParameters.begin(), CmdLineParameters.end(), PrintIt);

// Splice any flags from the original CmdLineParameters list into Flags list.
Flags.splice(Flags.begin(), CmdLineParameters, CmdLineParameters.begin(), StartOfFiles);

if (!Flags.empty()) {
cout << "Flags specified were:" << endl;
for_each(Flags.begin(), Flags.end(), PrintIt);
}
else {
cout << "No flags were specified" << endl;
}

// parameters list now contains only filenames. Splice them into FileNames list.
FileNames.splice(FileNames.begin(), CmdLineParameters, CmdLineParameters.begin(), CmdLineParameters.end());

if (!FileNames.empty()) {
cout << "Files specified (in order) were:" << endl;
for_each(FileNames.begin(), FileNames.end(), PrintIt);
}
else {
cout << "No files were specified" << endl;
}

// check if the help flag was specified
if (find_if(Flags.begin(), Flags.end(), IsHelpFlag())!=Flags.end()) {
cout << "The help flag was specified" << endl;
}

// open the files and do whatever you do

}

給出如許的敕令行:
test17 -w linux -o is -w great

輸入是:

The wrong number (3) of file names were specified
Command line parameters after stable partition
-w
-o
-w
linux
is
great
Flags specified were:
-w
-o
-w
Files specified (in order) were:
linux
is
great

------------------------------------------------------

18 結論
 
我們僅僅簡略的談了談你可以用list做的工作。我們沒有解釋一個對象的用戶界說類,固然這個不難。

假如你懂了適才說的這些算法面前的概念,那末你應用剩下的那些算法就應當沒有成績了。應用STL 最主要的器械就是獲得根本實際。

STL的症結現實上是iterator。STL算法作為參數應用iterator,他們指出一個規模,有時是一個規模, 有時是兩個。STL容器支撐iterator,這就是為何我們說 list<int>::iterator, 或 list<char>::iterator, 或 list<string>::iterator.

iterator有很好的界說繼續性。它們異常有效。某些iterator僅支撐對一個容器只讀,某些 僅支撐寫,還有一些僅能向前指,有一些是雙向的。有一些iterator支撐對一個容器的隨機存取。

STL算法須要某個iterator作為“動力” 假如一個容器不供給iterator作為“動力”,那末這個算法將沒法編譯。例如,list容器僅供給雙向的 iterator。平日的sort()算法須要隨機存取的iterator。這就是為何我們須要一個特殊的list成員函數sort()。

要適合的現實應用STL,你須要細心進修各類分歧的iterator。你須要曉得每種容器都支撐那類iterator。 你還須要曉得算法須要那種iterator,你固然也須要理解你可以有那種iterator。

在field中應用STL
客歲,我曾用STL寫過幾個貿易法式。它在許多方面削減了我的任務量,也消除了許多邏輯毛病。
最年夜的一個法式有年夜約5000行。能夠最驚人的工作就是它的速度。它讀入並處置一個1-2兆的申報文件僅花年夜約20秒。我是在linux上用gcc2.7.2開辟的,如今運轉在HP-UX機械上。它一共用了年夜約50和函數對象和許多容器,這些容器的年夜小從小list到一個有14,000個元素的map都有。

一個法式中的函數對象是處於一個繼續樹中,頂層的函數對象挪用低層的函數對象。我年夜量的應用STL算法for_each() ,find(),find_if(),count()和count_if(),我盡可能削減應用法式外部的函數,而應用STL的算法挪用。

STL偏向於主動的把代碼組織成清楚的掌握和支撐模塊。經由過程當心應用函數對象並給它們起成心義的名字,我使它們在我的軟件的掌握流中活動。

還有許多關於STL編程要曉得的器械,我願望你經由過程這些例子可以高興的任務。
參考數量中的兩本書在web上都有刊誤表,你可以本身糾正它們。
Stroustrup在每章前面都有個建議欄,特殊是關於出學者有效。副本書比晚期的版本加倍健談。它也更年夜了。書店裡還可以找到其他幾本關於STL的教科書。去看看,或許你能發明甚麼。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved