Stl 學習

Stl 學習

提供了類型安全、高效而易用特性的STL無疑是最值得C++程序員驕傲的部分。每一個C++程序員都應該好好學習STL).   

STLStandard Template Library 標準模板庫)是C++標準庫的一個重要組成部分,它由Stepanov and Lee等人最先開發,它是與C++幾乎同時開始開發的;一開始STL選擇了Ada作爲實現語言,但Ada有點不爭氣,最後他們選擇了C++,一個原因了,C++中已經有了模板。在後來,STL又被添加進了C++庫。1996年,惠普公司又免費公開了STL,爲STL的推廣做了很大的貢獻。

STL大體上包括container(容器)、algorithm(算法)和iterator(迭代器),容器和算法通過迭代器可以進行無縫連接。  

STL體現了型編程的思想,它具有高度的可重用性,高性能,高移植性。程序員不用思考具體實現過程,只要能夠熟練的應用就OK了。(有興趣研究具體實現的,可以看侯捷老師編著的《STL源碼剖析》)這樣他們就可以把精力放在程序開發的別的方面。

我非常佩服創造STL的那些計算機和數學精英。因爲他們做了一件非常辛苦的事情―――抽象概念。而STL就是通過把容器抽象爲統一的接口,算法利用這個接口,通過迭代器來操縱容器。因爲接口不變,實現的容器可以隨意更改。這樣,就爲編程、調試和擴展提供了便利。也許有一天,我們生產軟件的時候也可以想DIY一臺PC一樣簡單,只要拿來相應的實現模塊,通過簡單的拼裝和調試就可以創造一個軟件。這是多麼令人興奮的一件事^_^.不過,到時候,可能會有很多程序員失業了。呵呵,畢竟編寫類庫不需要很多的人員。

雖然STL不是面向對象的,但,我想,每個人都會爲它的創造力和高性能而感到興奮和折服。

一、容器 
作爲STL的最主要組成部分--容器,分爲向量(vector),雙端隊列(deque),表(list),隊列(queue),堆棧(stack),集合(set),多重集合(multiset),映射(map),多重映射(multimap)

容器    

特性

所在頭文件

向量vector

可以用常數時間訪問和修改任意元素,在序列尾部進行插入和刪除時,具有常數時間複雜度,對任意項的插入和刪除就有的時間複雜度與到末尾的距離成正比,尤其對向量頭的添加和刪除的代價是驚人的高的

<vector>

雙端隊列deque

基本上與向量相同,唯一的不同是,其在序列頭部插入和刪除操作也具有常量時間複雜度

<deque>

list

對任意元素的訪問與對兩端的距離成正比,但對某個位置上插入和刪除一個項的花費爲常數時間。

<list>

隊列queue

插入只可以在尾部進行,刪除、檢索和修改只允許從頭部進行。按照先進先出的原則。

<queue>

堆棧stack

堆棧是項的有限序列,並滿足序列中被刪除、檢索和修改的項只能是最近插入序列的項。即按照後進先出的原則

<stack>

集合set

由節點組成的紅黑樹,每個節點都包含着一個元素,節點之間以某種作用於元素對的謂詞排列,沒有兩個不同的元素能夠擁有相同的次序,具有快速查找的功能。但是它是以犧牲插入車刪除操作的效率爲代價的

<set>

多重集合multiset

和集合基本相同,但可以支持重複元素具有快速查找能力

<set>

映射map

{鍵,值}對組成的集合,以某種作用於鍵對上的謂詞排列。具有快速查找能力

<map>

多重集合multimap

比起映射,一個鍵可以對應多了值。具有快速查找能力

<map>

考慮到不同的實際需要,更主要的是效率的需要,我們可以選擇不同的容器來實現我們的程序,以此達到我們提高性能的目的。這也是用好STL的一個難點,但這也是關鍵。

二、算法
算法部分主要由頭文件<algorithm><numeric><functional>組成。<algorithm>是所有STL頭文件中最大的一個,它是由一大堆模版函數組成的,可以認爲每個函數在很大程度上都是獨立的,其中常用到的功能範圍涉及到比較、交換、查找、遍歷操作、複製、修改、移除、反轉、排序、合併等等。<numeric>體積很小,只包括幾個在序列上面進行簡單數學運算的模板函數,包括加法和乘法在序列上的一些操作。<functional>中則定義了一些模板類,用以聲明函數對象。

STL的算法也是非常優秀的,它們大部分都是類屬的,基本上都用到了C++的模板來實現,這樣,很多相似的函數就不用自己寫了,只要用函數模板就OK了。

我們使用算法的時候,要針對不同的容器,比如:對集合的查找,最好不要用通用函數find,它對集合使用的時候,性能非常的差,最好用集合自帶的find函數,它針對了集合進行了優化,性能非常的高。

三、迭代器
它的具體實現在<itertator> 中,我們完全可以不管迭代器類是怎麼實現的,大多數的時候,把它理解爲指針是沒有問題的(指針是迭代器的一個特例,它也屬於迭代器),但是,決不能完全這麼做。

迭代器功能Abilities Of Iterator Gategories

輸入迭代器

Input iterator

向前讀

Reads forward

istream

輸出迭代器

Output iterator

向前寫

Writes forward

ostream,inserter

前向迭代器

Forward iterator

向前讀寫

Read and Writes forward

 

雙向迭代器

Bidirectional iterator

向前向後讀寫

Read and Writes forward and

backward

list,set,multiset,map,mul

timap

隨機迭代器

Random access iterator

隨機讀寫

Read and Write with random

access

vector,deque,array,string

由此可見,指針和迭代器還是有很大差別的。和指針最接近的就是隨機訪問迭代器。下面是一個我編寫的小例子:功能是分別對數組,向量,表,多重集合進行插入操作,對每個容器插入100萬個隨機整數;

#include<iostream>
#include<iterator>
#include<vector>
#include<list>
#include<set>
#include<time.h>
#include<conio.h>
#include<algorithm>
using namespace std;
template<typename T>
void arrayInsert(T*a,T*s,long size)    //向數組插入數據
{
   //for(long i=0;i<10;i++)  // //好像數組支持不到100萬,我們就算10萬的
                                         //最後在把把結果乘以10吧,
   //{
       for(long k=0;k<size;k++)
       {
          
a[k]=s[k]; 
       }
 
  //}
}
template<typename T>
void vectorInsert( vector<T> *v,T*s,long size)     //向向量插入數據
{
   for(int i=0;i<10;i++)
   {
       for(long k=0;k<size;k++)
       {
          
v->push_back(s[k]);
      }
   }
}
template<typename T>
void listInsert(list<T>*l,T*s,long size)   //向表插入數據
{
   for(int i=0;i<10;i++)
   {
       for(long k=0;k<size;k++)
       {
          
l->push_back(s[k]);
       }  
   }
}
template<class T>
void multisetInsert(multiset<T>*s1,T*s,long size)   //向多重集合插入數據
{
   for(int i=0;i<10;i++)
   {
       for(long k=0;k<size;k++)
       {
           s1->insert(s[k]);  
       }
   }
}
int* genIntData(long size)                  //生成隨機數
{
   int* data=new int[size];
  
generate(&data[0],&data[size],rand);
   return data;

}  
int main(void)
{
   const long size=100000;
  
int* s_data,array1[size];
  
double begin,end;
   s_data=genIntData(size);
   vector<int> vector1;
  
list<int> list1;
  
multiset<int> multiset1;
   clock();
  
cout<<"?"<<endl

 //開始計算數組插入時間 

  getch();
   begin=(double)clock()/CLK_TCK;
  
arrayInsert<int>(array1,s_data,size);
  
end=(double)clock()/CLK_TCK;
   cout<<"??"<<(end-begin)<<endl;

   //開始計算向量插入時間
  
getch();
   begin=(double)clock()/CLK_TCK;
   vectorInsert<int>(&vector1,s_data,size);
  
end=(double)clock()/CLK_TCK;
  
cout<<"??"<<(end-begin)<<endl;

   //開始計算list插入時間
    
getch();
  
begin=(double)clock()/CLK_TCK;
  
listInsert<int>(&list1,s_data,size);
  
end=(double)clock()/CLK_TCK;
  
cout<<"??"<<(end-begin)<<endl;

   //開始計算multiset插入時間
  
getch();
  
begin=(double)clock()/CLK_TCK;
  
multisetInsert<int>(&multiset1,s_data,size);
  
end=(double)clock()/CLK_TCK;
  
cout<<"??"<<(end-begin)<<endl;

   getch();
  
free(s_data);
  
return 0;      
}

 

這個程序清晰的表明這幾種容器在插入速度之間的差別,當然,每種容器不是萬能的,不能一好百好。比如說,多集在查找方面的優勢是其他序列容器不可比擬的。  還有,最好不要試圖超越STL,因爲:
1
STL實現使用的是最佳算法。它是幾乎完美的。
2
STL實現的設計者通常是相應領域的專家。
3
、各領域的專家致力於提供靈活的、強大和高效的庫。這是他們的首要的任務。對於,我們這些其餘的人,開發可重用的容器和算法頂多只算第二個目標。我們的首要任務是交付緊扣主題的應用程序。大多數情況下,我們沒有時間和專門的技術去和那些專家相比。

但是,超越STL不是不可能的。但是一般情況下,你只能靠犧牲可移植性來提高性能,這對於很多情況來說是很不好的。爲了,超越STL,我們要付出非常大的努力。而且,最好我們知道一些STL專家們不知道的東西,爾後我們可以有針對性的進行優化,否則,我們的努力完全有可能白費。

面對這樣一個優秀的庫,並且它是免費的。我們C++程序員沒有理由拒絕它,甚至去自己開發一個庫。

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章