STL

STL簡介

1.做爲一個C++程序設計者,STL是一種不可忽視的技術。html

Standard Template Library (STL):標準模板庫,更準確的說是 C++ 程序設計語言標準模板庫。STL是全部C++編譯器和全部操做系統平臺都支持的一種庫,說它是一種庫是由於,雖然STL是一種標準,也就是說對全部的編譯器來講,提供給C++程序設計者的接口都是同樣的。也就是說同一段STL代碼在不一樣編譯器和操做系統平臺上運行的結果都是相同的,可是底層實現能夠是不一樣的。使人興奮的是,STL的使用者並不須要瞭解它的底層實現。 試想一下,若是咱們有一把能打開全部鎖的鑰匙,那將是多麼使人瘋狂啊。ios

STL的目的是標準化組件,這樣你就不用從新開發它們了。你能夠僅僅使用這些現成的組件。STL如今是C++的一部分,所以不用額外安裝什麼。它被內建在你的編譯器以內。程序員

2.      爲何咱們須要學習STL算法

STL是 C++的ANSI/ISO 標準的一部分,能夠用於全部C++語言編譯器和全部平臺(Windows/Unix/Linux..)。STL的同一版本在任意硬件配置下都是可用的;數組

STL 提供了大量的可複用軟件組織。例如,程序員不再用本身設計排序,搜索算法了,這些都已是STL的一部分了。嘎嘎,有意思吧。數據結構

使用STL 的應用程序保證了獲得的實如今處理速度和內存利用方面都是高效的,由於STL設計者們已經爲咱們考慮好了。函數

使用STL編寫的代碼更容易修改和閱讀,這是固然的啦。由於代碼更短了,不少基礎工做代碼已經被組件化了;工具

使用簡單,雖然內部實現很複雜。組件化

雖然,STL的優勢甚多,可是STL的語法實在令初學者人頭疼,許多人望而卻步。但是STL是每一個C++程序設計者早晚都要啃的一塊骨頭。學習

3.      初識STL

下面讓咱們來看幾段代碼吧:

1 #include <iostream>
2 int main(void)
3 {
4     double a[] = {1, 2, 3, 4, 5};
5     std::cout<<mean(a, 5)<<std::endl;    // will print 3
6     return 0;
7 }

好懂吧,除了那個std有點讓人不舒服之外,這是一段普通的沒有使用STL的C++代碼。

再看下面一段:

 1 #include <vector>
 2 #include <iostream>
 3 int main()
 4 {
 5     std::vector<double> a;
 6     a.push_back(1);
 7     a.push_back(2);
 8     a.push_back(3);
 9     a.push_back(4);
10     a.push_back(5);
11     for(int i = 0; i < a.size(); ++i)
12 {
13         std::cout<<a[i]<<std::endl;
14     }
15     return 0;
16 }

若是你真的沒有接觸過STL的話,你會問,呀,vector 是啥呀?這是一段純種的STL代碼,看到尖括號了吧,知道那是模板了吧。看到a.push_back(5)、a.size()你不感受奇怪麼?但是咱們並無定義這些函數啊。

 1 #include <vector>
 2 #include <iostream>
 3 int main()
 4 {
 5     std::vector< int > q;
 6     q.push_back(10);
 7     q.push_back(11);
 8     q.push_back(12);
 9     std::vector< int > v;
10     for(int i=0; i<5; ++i){
11         v.push_back(i);
12     }
13     std::vector<int>::iterator it = v.begin() + 1;
14     it = v.insert(it, 33);
15     v.insert(it, q.begin(), q.end());
16     it = v.begin() + 3;
17     v.insert(it, 3, -1);
18     it = v.begin() + 4;
19     v.erase(it);
20     it = v.begin() + 1;
21     v.erase(it, it + 4);
22     v.clear();
23     return 0;
24 }

這一段你又看到了新東西了吧:iterator、insert、erase、clear。不羅嗦了,等你看完這篇文章,回頭再看就簡單了。

關於模板的其餘細節,讀者能夠參閱《C++ Templates 中文版》在這裏,簡單的介紹一下模板類和函數模板的概念。

模板是C++中實現代碼重用機制的一種工具,能夠實現類型參數化,把類型定義爲參數。函數模板和類模板容許用戶構造模板函數和模板類。

下面咱們來看一段函數模板的例子:

 

 1 #include<iostream>
 2 #include<string>
 3 using namespace std;
 4 //定義函數模板
 5 template<class T>   //template 是關鍵字,T 表示一種待實例化的類型
 6                     //template<typename T> 也是對的
 7 T MAX(T a, T b)//函數模板,函數名爲 max,此函數有2個T類型的參數,返回類型爲T
 8 {
 9 return (a>b)?a:b;
10 }
11 //在此例實例化的時候,T能夠是多種類型的,int,char,string…
12 int main()
13 {
14 int x=2,y=6;
15    double x1=9.123,y1=12.6543;
16    cout<<"把T實例化爲int:"<<MAX(x,y)<<endl;//實例化函數模板,把T實例化爲int
17    cout<<"把T實例化爲double:"<<MAX(x1,y1)<<endl; //把T實例化爲double
18 }
19 下面再看看,類模板:
20 #include<iostream>
21 using namespace std;
22 //定義名爲ex_class的類模板
23 template < typename T> class ex_class
24 {
25     T value;
26 public:
27     ex_class(T v) { value=v; }
28     void set_value(T v) { value=v; }
29     T get_value(void) {return value;}
30 };
31 //main()函數中測試ex_class類模板
32 int main()
33 {
34     //測試int類型數據
35     ex_class <int> a(5),b(10);
36     cout<<"a.value:"<<a.get_value()<<endl;
37     cout<<"b.value:"<<b.get_value()<<endl;
38     //測試char類型數據
39     ex_class <char> ch('A');
40     cout<<"ch.value:"<<ch.get_value()<<endl;
41     ch.set_value('a');
42     cout<<"ch.value:"<<ch.get_value()<<endl;
43     //測試double類型數據
44     ex_class <double> x(5.5);
45     cout<<"x.value:"<<x.get_value()<<endl;
46     x.set_value(7.5);
47     cout<<"x.value:"<<x.get_value()<<endl;
48 }

< STL>的組成

STL有三大核心部分:容器(Container)、算法(Algorithms)、迭代器(Iterator),容器適配器(container adaptor),函數對象(functor),除此以外還有STL其餘標準組件。通俗的講:

容器:裝東西的東西,裝水的杯子,裝鹹水的大海,裝人的教室……STL裏的容器是可容納一些數據的模板類。

算法:就是往杯子裏倒水,往大海里排污,從教室裏攆人……STL裏的算法,就是處理容器裏面數據的方法、操做。

迭代器:往杯子裏倒水的水壺,排污的管道,攆人的那個物業管理人員……STL裏的迭代器:遍歷容器中數據的對象。對存儲於容器中的數據進行處理時,迭代器能從一個成員移向另外一個成員。他能按預先定義的順序在某些容器中的成員間移動。對普通的一維數組、向量、雙端隊列和列表來講,迭代器是一種指針。

下面讓咱們來看看專家是怎麼說的:

容器(container):容器是數據在內存中組織的方法,例如,數組、堆棧、隊列、鏈表或二叉樹(不過這些都不是STL標準容器)。STL中的容器是一種存儲T(Template)類型值的有限集合的數據結構,容器的內部實現通常是類。這些值能夠是對象自己,若是數據類型T表明的是Class的話。

算法(algorithm):算法是應用在容器上以各類方法處理其內容的行爲或功能。例如,有對容器內容排序、複製、檢索和合並的算法。在STL中,算法是由模板函數表現的。這些函數不是容器類的成員函數。相反,它們是獨立的函數。使人吃驚的特色之一就是其算法如此通用。不只能夠將其用於STL容器,並且能夠用於普通的C++數組或任何其餘應用程序指定的容器。

迭代器(iterator):一旦選定一種容器類型和數據行爲(算法),那麼剩下惟一要他作的就是用迭代器使其相互做用。能夠把達代器看做一個指向容器中元素的普通指針。能夠如遞增一個指針那樣遞增迭代器,使其依次指向容器中每個後繼的元素。迭代器是STL的一個關鍵部分,由於它將算法和容器連在一塊兒。

下面我將依次介紹STL的這三個主要組件。

1.       容器

STL中的容器有隊列容器和關聯容器,容器適配器(congtainer adapters:stack,queue,priority queue),位集(bit_set),串包(string_package)等等。

  在本文中,我將介紹list,vector,deque等隊列容器,和set和multisets,map和multimaps等關聯容器,一共7種基本容器類。

  隊列容器(順序容器):隊列容器按照線性排列來存儲T類型值的集合,隊列的每一個成員都有本身的特有的位置。順序容器有向量類型、雙端隊列類型、列表類型三種。

u     基本容器——向量

向量(vector容器類):#include <vector>,vector是一種動態數組,是基本數組的類模板。其內部定義了不少基本操做。既然這是一個類,那麼它就會有本身的構造函數。vector 類中定義了4中種構造函數:

· 默認構造函數,構造一個初始長度爲0的空向量,如:vector<int> v1;

· 帶有單個整形參數的構造函數,此參數描述了向量的初始大小。這個構造函數還有一個可選的參數,這是一個類型爲T的實例,描述了各個向量種各成員的初始值;如:vector<int> v2(n,0); 若是預先定義了:n,他的成員值都被初始化爲0;

· 複製構造函數,構造一個新的向量,做爲已存在的向量的徹底複製,如:vector<int> v3(v2);

· 帶兩個常量參數的構造函數,產生初始值爲一個區間的向量。區間由一個半開區間[first,last) 來指定。如:vector<int> v4(first,last)

下面一個例子用的是第四種構造方法,其它的方法讀者能夠本身試試。

 1 //程序:初始化演示
 2 #include <cstring>
 3 #include <vector>
 4 #include <iostream>
 5 using namespace std;
 6 int ar[10] = { 12, 45, 234, 64, 12, 35, 63, 23, 12, 55 };
 7 char* str = "Hello World";
 8 int main()
 9 {
10     vector <int> vec1(ar, ar+10);   //first=ar,last=ar+10,不包括ar+10
11     vector < char > vec2(str,str+strlen(str)); //first=str,last= str+strlen(str),
12     cout<<"vec1:"<<endl;  
13     //打印vec1和vec2,const_iterator是迭代器,後面會講到
14     //固然,也能夠用for (int i=0; i<vec1.size(); i++)cout << vec[i];輸出
15     //size()是vector的一個成員函數
16     for(vector<int>::const_iterator p=vec1.begin();p!=vec1.end(); ++p)
17         cout<<*p;
18         cout<<'\n'<<"vec2:"<<endl;
19     for(vector< char >::const_iterator p1=vec2.begin();p1!=vec2.end(); ++p1)
20         cout<<*p1;
21     cout<<'\n';
22     return 0;
23 }     

爲了幫助理解向量的概念這裏寫了一個小例子其中用到了vector的成員函數:begin(),end(),push_back(),assign(),front(),back(),erase(),empty(),at(),size()

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 typedef vector<int> INTVECTOR;//自定義類型INTVECTOR
 5 //測試vector容器的功能
 6 int main()
 7 {
 8     //vec1對象初始爲空
 9     INTVECTOR vec1;  
10     //vec2對象最初有10個值爲6的元素
11     INTVECTOR vec2(10,6);
12     //vec3對象最初有3個值爲6的元素,拷貝構造
13     INTVECTOR vec3(vec2.begin(),vec2.begin()+3);
14     //聲明一個名爲i的雙向迭代器
15     INTVECTOR::iterator i;
16     //從前向後顯示vec1中的數據
17     cout<<"vec1.begin()--vec1.end():"<<endl;
18     for (i =vec1.begin(); i !=vec1.end(); ++i)
19         cout << *i << " ";
20     cout << endl;
21     //從前向後顯示vec2中的數據
22     cout<<"vec2.begin()--vec2.end():"<<endl;
23     for (i =vec2.begin(); i !=vec2.end(); ++i)
24         cout << *i << " ";
25     cout << endl;
26     //從前向後顯示vec3中的數據
27     cout<<"vec3.begin()--vec3.end():"<<endl;
28     for (i =vec3.begin(); i !=vec3.end(); ++i)
29         cout << *i << " ";
30     cout << endl;
31     //測試添加和插入成員函數,vector不支持從前插入
32    vec1.push_back(2);//從後面添加一個成員
33     vec1.push_back(4);
34     vec1.insert(vec1.begin()+1,5);//在vec1第一個的位置上插入成員5
35     //從vec1第一的位置開始插入vec3的全部成員
36     vec1.insert(vec1.begin()+1,vec3.begin(),vec3.end());
37     cout<<"after push() and insert() now the vec1 is:" <<endl;
38     for (i =vec1.begin(); i !=vec1.end(); ++i)
39         cout << *i << " ";
40     cout << endl;
41     //測試賦值成員函數
42     vec2.assign(8,1);   // 從新給vec2賦值,8個成員的初始值都爲1
43     cout<<"vec2.assign(8,1):" <<endl;
44     for (i =vec2.begin(); i !=vec2.end(); ++i)
45         cout << *i << " ";
46     cout << endl;
47     //測試引用類函數
48     cout<<"vec1.front()="<<vec1.front()<<endl;//vec1第零個成員
49     cout<<"vec1.back()="<<vec1.back()<<endl;//vec1的最後一個成員
50     cout<<"vec1.at(4)="<<vec1.at(4)<<endl;//vec1的第五個成員
51     cout<<"vec1[4]="<<vec1[4]<<endl;
52    //測試移出和刪除
53     vec1.pop_back();//將最後一個成員移出vec1
54     vec1.erase(vec1.begin()+1,vec1.end()-2);//刪除成員
55     cout<<"vec1.pop_back() and vec1.erase():" <<endl;
56     for (i =vec1.begin(); i !=vec1.end(); ++i)
57         cout << *i << " ";
58     cout << endl;
59     //顯示序列的狀態信息
60     cout<<"vec1.size(): "<<vec1.size()<<endl;//打印成員個數
61     cout<<"vec1.empty(): "<<vec1.empty()<<endl;//清空
62 }

push_back()是將數據放入vector(向量)或deque(雙端隊列)的標準函數。Insert()是一個與之相似的函數,然而它在全部容器中均可以使用,可是用法更加複雜。end()其實是取末尾加一,以便讓循環正確運行--它返回的指針指向最靠近數組界限的數據。

在Java裏面也有向量的概念。Java中的向量是對象的集合。其中,各元素能夠沒必要同類型,元素能夠增長和刪除,不能直接加入原始數據類型。

u     雙端隊列(qeque容器類):

deque(讀音:deck,意即:double queue,#include<qeque>)容器類與vector相似,支持隨機訪問和快速插入刪除,它在容器中某一位置上的操做所花費的是線性時間。與vector不一樣的是,deque還支持從開始端插入數據:push_front()。此外deque也不支持與vector的capacity()、reserve()相似的操做。

 1 #include <iostream>
 2 #include <deque>
 3 using namespace std;
 4 typedef deque<int> INTDEQUE;//有些人很討厭這種定義法,呵呵
 5 //從前向後顯示deque隊列的所有元素
 6 void put_deque(INTDEQUE deque, char *name)
 7 {
 8     INTDEQUE::iterator pdeque;//仍然使用迭代器輸出
 9     cout << "The contents of " << name << " : ";
10     for(pdeque = deque.begin(); pdeque != deque.end(); pdeque++)
11         cout << *pdeque << " ";//注意有 "*"號哦,沒有"*"號的話會報錯
12     cout<<endl;
13 }
14 //測試deqtor容器的功能
15 int main()
16 {
17     //deq1對象初始爲空
18     INTDEQUE deq1;  
19     //deq2對象最初有10個值爲6的元素
20     INTDEQUE deq2(10,6);
21     //聲明一個名爲i的雙向迭代器變量
22     INTDEQUE::iterator i;
23     //從前向後顯示deq1中的數據
24     put_deque(deq1,"deq1");
25     //從前向後顯示deq2中的數據
26     put_deque(deq2,"deq2");
27     //從deq1序列後面添加兩個元素
28     deq1.push_back(2);
29     deq1.push_back(4);
30     cout<<"deq1.push_back(2) and deq1.push_back(4):"<<endl;
31     put_deque(deq1,"deq1");
32     //從deq1序列前面添加兩個元素
33     deq1.push_front(5);
34     deq1.push_front(7);
35     cout<<"deq1.push_front(5) and deq1.push_front(7):"<<endl;
36     put_deque(deq1,"deq1");
37     //在deq1序列中間插入數據
38     deq1.insert(deq1.begin()+1,3,9);
39     cout<<"deq1.insert(deq1.begin()+1,3,9):"<<endl;
40     put_deque(deq1,"deq1");
41     //測試引用類函數
42     cout<<"deq1.at(4)="<<deq1.at(4)<<endl;
43     cout<<"deq1[4]="<<deq1[4]<<endl;
44     deq1.at(1)=10;
45     deq1[2]=12;
46     cout<<"deq1.at(1)=10 and deq1[2]=12 :"<<endl;
47     put_deque(deq1,"deq1");
48     //從deq1序列的先後各移去一個元素
49     deq1.pop_front();
50     deq1.pop_back();
51     cout<<"deq1.pop_front() and deq1.pop_back():"<<endl;
52     put_deque(deq1,"deq1");
53     //清除deq1中的第2個元素
54     deq1.erase(deq1.begin()+1);
55     cout<<"deq1.erase(deq1.begin()+1):"<<endl;
56     put_deque(deq1,"deq1");
57     //對deq2賦值並顯示
58     deq2.assign(8,1);
59     cout<<"deq2.assign(8,1):"<<endl;
60     put_deque(deq2,"deq2");
61 }

上面咱們演示了deque如何進行插入刪除等操做,像erase(),assign()是大多數容器都有的操做。關於deque的其餘操做請參閱其餘書籍。

u     表(List容器類)

 List(#include<list>)又叫鏈表,是一種雙線性列表,只能順序訪問(從前向後或者從後向前),圖2是list的數據組織形式。與前面兩種容器類有一個明顯的區別就是:它不支持隨機訪問。要訪問表中某個下標處的項須要從表頭或表尾處(接近該下標的一端)開始循環。並且缺乏下標預算符:operator[]。

  同時,list仍然包涵了erase(),begin(),end(),insert(),push_back(),push_front()這些基本函數,下面咱們來演示一下list的其餘函數功能。merge():合併兩個排序列表;splice():拼接兩個列表;sort():列表的排序。

 

 1 #include <iostream>
 2 #include <string>
 3 #include <list>
 4 using namespace std;
 5 void PrintIt(list<int> n)
 6 {
 7     for(list<int>::iterator iter=n.begin(); iter!=n.end(); ++iter)
 8       cout<<*iter<<" ";//用迭代器進行輸出循環
 9 }
10    
11 int main()
12 {
13     list<int> listn1,listn2;    //給listn1,listn2初始化
14     listn1.push_back(123);
15     listn1.push_back(0);
16     listn1.push_back(34);
17     listn1.push_back(1123);    //now listn1:123,0,34,1123
18     listn2.push_back(100);
19     listn2.push_back(12);    //now listn2:12,100
20     listn1.sort();
21     listn2.sort();    //給listn1和listn2排序
22     //now listn1:0,34,123,1123         listn2:12,100
23     PrintIt(listn1);
24     cout<<endl;
25     PrintIt(listn2);
26     listn1.merge(listn2);    //合併兩個排序列表後,listn1:0,12,34,100,123,1123
27     cout<<endl;
28     PrintIt(listn1);
29 }

queue Functions

Illustrates how to use the queue::push, queue::pop, queue::empty, queue::back, queue::front, and queue::size Standard Template Library (STL) functions in Visual C++.

queue::push( );
queue::pop( );
queue::empty( );
queue::back( );
queue::front( );
queue::size( );

Note   The class/parameter names in the prototype do not match the version in the header file. Some have been modified to improve readability.

Description

The sample shows the queue implementation using list and deque containers.

Example

 

 1 // queue.cpp
 2 // compile with: /EHsc
 3 //
 4 // Functions:
 5 //    queue::push(), queue::pop(), queue::empty(), queue::back(),
 6 //    queue::front(),queue::size()
 7 
 8 #include <list>
 9 #include <iostream>
10 #include <queue>
11 #include <deque>
12 
13 using namespace std ;
14 
15 // Using queue with list
16 typedef list<int > INTLIST;
17 typedef queue<int>  INTQUEUE;
18 
19 // Using queue with deque
20 typedef deque<char*> CHARDEQUE;
21 typedef queue<char*> CHARQUEUE;
22 
23 int main(void)
24 {
25     size_t size_q;
26     INTQUEUE q;
27     CHARQUEUE p;
28 
29     // Insert items in the queue(uses list)
30     q.push(42);
31     q.push(100);
32     q.push(49);
33     q.push(201);
34 
35     // Output the size of queue
36     size_q = q.size();
37     cout << "size of q is:" << size_q << endl;
38 
39     // Output items in queue using front()
40     // and use pop() to get to next item until
41     // queue is empty
42     while (!q.empty())
43     {
44         cout << q.front() << endl;
45         q.pop();
46     }
47 
48 // Insert items in the queue(uses deque)
49     p.push("cat");
50     p.push("ape");
51     p.push("dog");
52     p.push("mouse");
53     p.push("horse");
54 
55     // Output the item inserted last using back()
56     cout << p.back() << endl;
57 
58     // Output the size of queue
59     size_q = p.size();
60     cout << "size of p is:" << size_q << endl;
61 
62     // Output items in queue using front()
63     // and use pop() to get to next item until
64     // queue is empty
65     while (!p.empty())
66     {
67         cout << p.front() << endl;
68         p.pop();
69     }
70 }

Output

size of q is:4
42
100
49
201
horse
size of p is:5
cat
ape
dog
mouse
horse
相關文章
相關標籤/搜索