STL標准入門詳解


第一部分:(參考百度百科)

一、STL簡介

STL(Standard Template Library,標准模板庫)是惠普實驗室開發的一系列軟件的統稱。它是由Alexander Stepanov、Meng Lee和

David R Musser在惠普實驗室工作時所開發出來的。現在雖說它主要出現在C++中,但在被引入C++之前該技術就已經存在了很長的

一段時間。

STL的代碼從廣義上講分為三類:algorithm(算法)、container(容器)和iterator(迭代器),幾乎所有的代碼都采用了模板類

和模版函數的方式,這相比於傳統的由函數和類

組成的庫來說提供了更好的代碼重用機會。在C++標准中,STL被組織為下面的13個頭文

件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、

<memory>、<numeric>、<queue>、<set>、<stack>和<utility>。 

二、算法

大家都能取得的一個共識是函數庫對數據類型的選擇對其可重用性起着至關重要的作用。舉例來說,一個求方根的函數,在使用浮

點數作為其參數類型的情況下的可重用性肯定比使用整型作為它的參數類性要高。而C++通過模板的機制允許推遲對某些類型的選

擇,直到真正想使用模板或者說對模板進行特化的時候,STL就利用了這一點提供了相當多的有用算法。它是在一個有效的框架中完

成這些算法的——你可以將所有的類型划分為少數的幾類,然后就可以在模版的參數中使用一種類型替換掉同一種類中的其他類型。

STL提供了大約100個實現算法的模版函數,比如算法for_each將為指定序列中的每一個元素調用指定的函數,stable_sort以你所指

定的規則對序列進行穩定性排序等等。這樣一來,只要我們熟悉了STL之后,許多代碼可以被大大的化簡,只需要通過調用一兩個算

法模板,就可以完成所需要的功能並大大地提升效率。

算法部分主要由頭文件<algorithm>,<numeric>和<functional>組成。

<algorithm>是所有STL頭文件中最大的一個(盡管它很好理解),它是由一大堆模版函數組成的,可以認為每個函數在很大程度上

都是獨立的,其中常用到的功能范圍涉及到比較、交換、查找、遍歷操作、復制、修改、移除、反轉、排序、合並等等。

<numeric>體積很小,只包括幾個在序列上面進行簡單數學運算的模板函數,包括加法和乘法在序列上的一些操作。<functional>中

則定義了一些模板類,用以聲明函數對象。

 

三、容器

在實際的開發過程中,數據結構本身的重要性不會遜於操作於數據結構的算法的重要性,當程序中存在着對時間要求很高的部分

時,數據結構的選擇就顯得更加重要。

經典的數據結構數量有限,但是我們常常重復着一些為了實現向量、鏈表等結構而編寫的代碼,這些代碼都十分相似,只是為了適

應不同數據的變化而在細節上有所出入。STL容器就為我們提供了這樣的方便,它允許我們重復利用已有的實現構造自己的特定類型

下的數據結構,通過設置一些模版類,STL容器對最常用的數據結構提供了支持,這些模板的參數允許我們指定容器中元素的數據類

型,可以將我們許多重復而乏味的工作簡化。

容器部分主要由頭文件<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>組成。對於常用的一些容器和容器適配器(可以

看作由其它容器實現的容器),可以通過下表總結一下它們和相應頭文件的對應關系。

 

向量(vector) 連續存儲的元素<vector>

列表(list)       由節點組成的雙向鏈表,每個結點包含着一個元素<list>

雙隊列(deque) 連續存儲的指向不同元素的指針所組成的數組<deque>

集合(set) 由節點組成的紅黑樹,每個節點都包含着一個元素,節點之間以某種作用於元素對的謂詞排列,沒有兩個不同的元素能

夠擁有相同的次序 <set>

多重集合(multiset) 允許存在兩個次序相等的元素的集合 <set>

棧(stack) 后進先出的值的排列 <stack>

隊列(queue) 先進先出的執的排列 <queue>

優先隊列(priority_queue) 元素的次序是由作用於所存儲的值對上的某種謂詞決定的的一種隊列 <queue>

映射(map) 由{鍵,值}對組成的集合,以某種作用於鍵對上的謂詞排列 <map>

多重映射(multimap) 允許鍵對有相等的次序的映射 <map>

 

四、迭代器

下面要說的迭代器從作用上來說是最基本的部分,可是理解起來比前兩者都要費力一些(至少筆者是這樣)。軟件設計有一個基本

原則,所有的問題都可以通過引進一個間接層來簡化,這種簡化在STL中就是用迭代器來完成的。

概括來說,迭代器在STL中用來將算法和容器聯系起來,起着一種黏和劑的作用。幾乎STL提供的所有算法都是通過迭代器存取元素

序列進行工作的,每一個容器都定義了其本身所專有的迭代器,用以存取容器中的元素。

 

迭代器部分主要由頭文件<utility>,<iterator>和<memory>組成。

<utility>是一個很小的頭文件,它包括了貫穿使用在STL中的幾個模板的聲明,

<iterator>中提供了迭代器使用的許多方法,而對於<memory>的描述則十分的困難,它以不同尋常的方式為容器中的元素分配存儲

空間,同時也為某些算法執行期間產生的臨時對象提供機制,<memory>中的主要部分是模板類allocator,它負責產生所有容器中的

默認分配器。

   對於之前不太了解STL的讀者來說,上面的文字只是十分概括地描述了一下STL的框架,對您理解STL的機制乃至使用STL所起到的

幫助微乎甚微,這不光是因為深入STL需要對C++的高級應用有比較全面的了解,更因為STL的三個部分算法、容器和迭代器三部分是

互相牽制或者說是緊密結合的。從概念上講最基礎的部分是迭代器,可是直接學習迭代器會遇到許多抽象枯燥和繁瑣的細節,然而

不真正理解迭代器又是無法直接進入另兩部分的學習的(至少對剖析源碼來說是這樣)。可以說,適應STL處理問題的方法是需要花

費一定的時間的,但是以此為代價,STL取得了一種十分可貴的獨立性,它通過迭代器能在盡可能少地知道某種數據結構的情況下完

成對這一結構的運算,所以下決心鑽研STL的朋友們千萬不要被一時的困難擊倒。其實STL運用的模式相對統一,只要適應了它,從

一個STL工具到另一個工具,都不會有什么大的變化。

對於STL的使用,也普遍存在着兩種觀點。第一種認為STL的最大作用在於充當經典的數據結構和算法教材,因為它的源代碼涉及了

許多具體實現方面的問題。第二種則認為STL的初衷乃是為了簡化設計,避免重復勞動,提高編程效率,因此應該是“應用至”的,

對於源代碼則不必深究。筆者則認為分析源代碼和應用並不矛盾,通過分析源代碼也能提高我們對其應用的理解,當然根據具體的

目的也可以有不同的側重。

第二部分:

暫且舉幾個非常容易理解的程序源碼:

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 int main(){
 5     vector<int>vi;
 6     int a;
 7     while(true)
 8     {
 9     cout<<"輸入一個整數,按0停止輸入:";
10     cin>>a;
11     if(a==0)
12     break;
13     vi.push_back(a);   
14     vector<int>::iterator iter;
15     for(iter=vi.begin();iter!=vi.end();++iter)
16     cout<<*iter;      
17     }
18     return 0;
19     }
View Code
 1 #include <iostream> 
 2 #include <string>
 3 #include <vector> 
 4 using namespace std; 
 5 int main(){
 6 string str="shiyang"; 
 7 vector <string> vecstr; 
 8 vecstr.push_back(str);
 9 vector <string> ::iterator iter= vecstr.begin(); 
10 cout<<*iter<<endl;
11 return 0;
12 }
View Code
#include   <stdlib.h>   
#include   <windows.h>   
#include   <conio.h>   
#include   <map>   //STL
#include   <functional> //STL  
#include   <algorithm>   //STL
#include   <iostream>   
using namespace std;   
typedef  map<int,int*> m_iip;   
typedef  map<int,char*> m_icp;   
class  f_c{   
  int _i;   
  public:   
  f_c(int i):_i(i){
          
  }   
  void operator()(m_iip::value_type ite)   
  {   
  cout<<_i++<<"\t"<<ite.first<<" shi"<<endl;   
  }   
  void operator()(m_icp::value_type ite)  
  {   
  cout<<_i++<<"\t"<<ite.first<<" yang"<<endl;  
  }   
  };   
  void f(int i,int c)   
  {   
      
  }   
  int main(int argc,char* argv[]){
  m_iip  iip;   
  m_icp  icp;   
  int i=0;   
  iip.insert(make_pair(34,&i));   
  iip.insert(make_pair(67,&i));   
  iip.insert(make_pair(5,&i));   
  iip.insert(make_pair(342,&i));    
  char d=0;   
  icp.insert(make_pair(12,&d));   
  icp.insert(make_pair(54,&d));   
  icp.insert(make_pair(6,&d));   
  icp.insert(make_pair(92,&d));   
  for_each(iip.begin(),iip.end(),f_c(8));   
  for_each(icp.begin(),icp.end(),f_c(65));//   
  return 0;   
  }
View Code
 1 #include <iostream>
 2 #include <list>
 3 #include <numeric>
 4 #include <algorithm>
 5 using namespace std;
 6 //創建一個list容器的實例LISTINT
 7 typedef list<int> LISTINT;
 8 
 9 //創建一個list容器的實例LISTCHAR
10 typedef list<int> LISTCHAR;
11 
12 int main(){
13     //--------------------------
14     //用list容器處理整型數據
15     //--------------------------
16     //用LISTINT創建一個名為listOne的list對象
17     LISTINT listOne;
18     //聲明i為迭代器
19     LISTINT::iterator i;
20 
21     //從前面向listOne容器中添加數據
22     listOne.push_front (2);
23     listOne.push_front (1);
24 
25     //從后面向listOne容器中添加數據
26     listOne.push_back (3);
27     listOne.push_back (4);
28 
29     //從前向后顯示listOne中的數據
30     cout<<"listOne.begin()--- listOne.end():"<<endl;
31     for (i = listOne.begin(); i != listOne.end(); ++i)
32         cout << *i << " ";
33     cout << endl;
34 
35     //從后向前顯示listOne中的數據
36 LISTINT::reverse_iterator ir;
37     cout<<"listOne.rbegin()---listOne.rend():"<<endl;
38     for (ir =listOne.rbegin(); ir!=listOne.rend();ir++) {
39         cout << *ir << " ";
40     }
41     cout << endl;
42 
43     //使用STL的accumulate(累加)算法
44     int result = accumulate(listOne.begin(), listOne.end(),0);
45     cout<<"Sum="<<result<<endl;
46     cout<<"------------------"<<endl;
47 
48     //--------------------------
49     //用list容器處理字符型數據
50     //--------------------------
51 
52     //用LISTCHAR創建一個名為listOne的list對象
53     LISTCHAR listTwo;
54     //聲明i為迭代器
55     LISTCHAR::iterator j;
56 
57     //從前面向listTwo容器中添加數據
58     listTwo.push_front ('A');
59     listTwo.push_front ('B');
60 
61     //從后面向listTwo容器中添加數據
62     listTwo.push_back ('x');
63     listTwo.push_back ('y');
64 
65     //從前向后顯示listTwo中的數據
66     cout<<"listTwo.begin()---listTwo.end():"<<endl;
67     for (j = listTwo.begin(); j != listTwo.end(); ++j)
68         cout << char(*j) << " ";
69     cout << endl;
70 
71     //使用STL的max_element算法求listTwo中的最大元素並顯示
72     j=max_element(listTwo.begin(),listTwo.end());
73     cout << "The maximum element in listTwo is: "<<char(*j)<<endl;
74 }
View Code
 1 #include <iostream>
 2 #include <list>
 3 
 4 using namespace std;
 5 typedef list<int> INTLIST;
 6 
 7 //從前向后顯示list隊列的全部元素
 8 void put_list(INTLIST list, char *name)
 9 {
10     INTLIST::iterator plist;
11 
12     cout << "The contents of " << name << " : ";
13     for(plist = list.begin(); plist != list.end(); plist++)
14         cout << *plist << " ";
15     cout<<endl;
16 }
17 
18 //測試list容器的功能
19 int main(){
20 //list1對象初始為空
21     INTLIST list1;   
22     //list2對象最初有10個值為6的元素 
23     INTLIST list2(10,6); 
24     //list3對象最初有9個值為6的元素 
25     INTLIST list3(list2.begin(),--list2.end()); 
26 
27     //聲明一個名為i的雙向迭代器
28     INTLIST::iterator i;
29 
30     //從前向后顯示各list對象的元素
31     put_list(list1,"list1");
32     put_list(list2,"list2");
33     put_list(list3,"list3");
34     
35 //從list1序列后面添加兩個元素
36 list1.push_back(2);
37 list1.push_back(4);
38 cout<<"list1.push_back(2) and list1.push_back(4):"<<endl;
39     put_list(list1,"list1");
40 
41 //從list1序列前面添加兩個元素
42 list1.push_front(5);
43 list1.push_front(7);
44 cout<<"list1.push_front(5) and list1.push_front(7):"<<endl;
45     put_list(list1,"list1");
46 
47 //在list1序列中間插入數據3個9 
48 list1.insert(++list1.begin(),3,9);
49 cout<<"list1.insert(list1.begin(),3,9):"<<endl;
50     put_list(list1,"list1");
51 
52 //測試引用類函數
53 cout<<"list1.front()="<<list1.front()<<endl;
54 cout<<"list1.back()="<<list1.back()<<endl;
55 
56 //從list1序列的前后各移去一個元素
57 list1.pop_front();
58 list1.pop_back();
59 cout<<"list1.pop_front() and list1.pop_back():"<<endl;
60     put_list(list1,"list1");
61 
62 //清除list1中的第2個元素
63 list1.erase(++list1.begin());
64 cout<<"list1.erase(++list1.begin()):"<<endl;
65     put_list(list1,"list1");
66 
67 //對list2賦值並顯示
68 list2.assign(8,1);
69 cout<<"list2.assign(8,1):"<<endl;
70     put_list(list2,"list2");
71 
72 //顯示序列的狀態信息
73 cout<<"list1.max_size(): "<<list1.max_size()<<endl;
74 cout<<"list1.size(): "<<list1.size()<<endl;
75 cout<<"list1.empty(): "<<list1.empty()<<endl;
76 
77 //list序列容器的運算
78     put_list(list1,"list1");
79     put_list(list3,"list3");
80 cout<<"list1>list3: "<<(list1>list3)<<endl;
81 cout<<"list1<list3: "<<(list1<list3)<<endl;
82 
83 //對list1容器排序
84 list1.sort();
85     put_list(list1,"list1");
86     
87 //結合處理
88 list1.splice(++list1.begin(), list3);
89     put_list(list1,"list1");
90     put_list(list3,"list3"); 
91 }
View Code

第三部分:

50條忠告:(其中有幾條覺得寫的不夠貼切,所以刪了,發了余下的部分)

1.把C++當成一門新的語言學習;

2.看《Thinking In C++》,不要看《C++變成死相》;

3.看《The C++ Programming Language》和《Inside The C++ Object Model》,不要因為他們很難而我們自己是初學者所以就不

看;

4.不要被VC、BCB、BC、MC、TC等詞匯所迷惑——他們都是集成開發環境,而我們要學的是一門語言;

5.不要放過任何一個看上去很簡單的小編程問題——他們往往並不那么簡單,或者可以引伸出很多知識點;

6.會用Visual C++,並不說明你會C++;

7.學class並不難,template、STL、generic programming也不過如此——難的是長期堅持實踐和不遺余力的博覽群書;

8.如果不是天才的話,想學編程就不要想玩游戲——你以為你做到了,其實你的C++水平並沒有和你通關的能力一起變高——其實可

以時刻記住:學C++是為了編游戲的;

9.看Visual C++的書,是學不了C++語言的;  

16.把時髦的技術掛在嘴邊,還不如把過時的技術記在心里;

18.學習編程最好的方法之一就是閱讀源代碼;

19.在任何時刻都不要認為自己手中的書已經足夠了;

20.請閱讀《The Standard C++ Bible》(中文版:標准C++寶典),掌握C++標准;

21.看得懂的書,請仔細看;看不懂的書,請硬着頭皮看;

22.別指望看第一遍書就能記住和掌握什么——請看第二遍、第三遍;

23.請看《Effective C++》和《More Effective C++》以及《Exceptional C++》;

24.不要停留在集成開發環境的搖籃上,要學會控制集成開發環境,還要學會用命令行方式處理程序;

25.和別人一起討論有意義的C++知識點,而不是爭吵XX行不行或者YY與ZZ哪個好;

26.請看《程序設計實踐》,並嚴格的按照其要求去做;

27.不要因為C和C++中有一些語法和關鍵字看上去相同,就認為它們的意義和作用完全一樣;

28.C++絕不是所謂的C的“擴充”——如果C++一開始就起名叫Z語言,你一定不會把C和Z語言聯系得那么緊密;

29.請不要認為學過XX語言再改學C++會有什么問題——你只不過又在學一門全新的語言而已;

30.讀完了《Inside The C++ Object Model》以后再來認定自己是不是已經學會了C++;

31.學習編程的秘訣是:編程,編程,再編程;

32.請留意下列書籍:《C++面向對象高效編程(C++ Effective Object-Oriented Software Construction)》《面向對象軟件構造

(Object-Oriented Software Construction)》《設計模式(Design Patterns)》《The Art of Computer Programming》; 

34.請把書上的程序例子親手輸入到電腦上實踐,即使配套光盤中有源代碼;

35.把在書中看到的有意義的例子擴充;

36.請重視C++中的異常處理技術,並將其切實的運用到自己的程序中;

37.經常回顧自己以前寫過的程序,並嘗試重寫,把自己學到的新知識運用進去;

38.不要漏掉書中任何一個練習題——請全部做完並記錄下解題思路;

39.C++語言和C++的集成開發環境要同時學習和掌握;

40.既然決定了學C++,就請堅持學下去,因為學習程序設計語言的目的是掌握程序設計技術,而程序設計技術是跨語言的;

41.就讓C++語言的各種平台和開發環境去激烈的競爭吧,我們要以學習C++語言本身為主;

42.當你寫C++程序寫到一半卻發現自己用的方法很拙劣時,請不要馬上停手;請盡快將余下的部分粗略的完成以保證這個設計的完

整性,然后分析自己的錯誤並重新設計和編寫(參見43);

43.別心急,設計C++的class確實不容易;自己程序中的class和自己的class設計水平是在不斷的編程實踐中完善和發展的;

44.決不要因為程序“很小”就不遵循某些你不熟練的規則——好習慣是培養出來的,而不是一次記住的;

45.每學到一個C++難點的時候,嘗試着對別人講解這個知識點並讓他理解——你能講清楚才說明你真的理解了;

46.記錄下在和別人交流時發現的自己忽視或不理解的知識點;

47.請不斷的對自己寫的程序提出更高的要求,哪怕你的程序版本號會變成Version 100.XX;

48.保存好你寫過的所有的程序——那是你最好的積累之一;

49.請不要做浮躁的人;

50.請熱愛C++!

第四部分:

C++頭文件一覽 

C、傳統 C++

#include <assert.h>    設定插入點
#include <ctype.h>     字符處理
#include <errno.h>     定義錯誤碼
#include <float.h>     浮點數處理
#include <fstream.h>    文件輸入/輸出
#include <iomanip.h>    參數化輸入/輸出
#include <iostream.h>   數據流輸入/輸出
#include <limits.h>    定義各種數據類型最值常量
#include <locale.h>    定義本地化函數
#include <math.h>     定義數學函數
#include <stdio.h>     定義輸入/輸出函數
#include <stdlib.h>    定義雜項函數及內存分配函數
#include <string.h>    字符串處理
#include <strstrea.h>   基於數組的輸入/輸出
#include <time.h>     定義關於時間的函數
#include <wchar.h>     寬字符處理及輸入/輸出
#include <wctype.h>    寬字符分類

標准 C++ 

#include <algorithm>     通用算法
#include <bitset>      位集容器
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>     復數類
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>      雙端隊列容器
#include <exception>    異常處理類
#include <fstream>
#include <functional>    定義運算函數(代替運算符)
#include <limits>
#include <list>       線性列表容器
#include <map>       映射容器
#include <iomanip>
#include <ios>      基本輸入/輸出支持
#include <iosfwd>     輸入/輸出系統使用的前置聲明
#include <iostream>
#include <istream>     基本輸入流
#include <ostream>     基本輸出流
#include <queue>       隊列容器
#include <set>       集合容器
#include <sstream>     基於字符串的流
#include <stack>      堆棧容器    
#include <stdexcept>    標准異常類
#include <streambuf>   底層輸入/輸出支持
#include <string>     字符串類
#include <utility>     通用模板類
#include <vector>     動態數組容器
#include <cwchar>
#include <cwctype>

C99 增加

#include <complex.h>  復數處理
#include <fenv.h>    浮點環境
#include <inttypes.h>  整數格式轉換
#include <stdbool.h>   布爾環境
#include <stdint.h>   整型環境
#include <tgmath.h>  通用類型數學宏


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM