一、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>.以下筆者就簡單介紹一下STL各個部分的主要特點。
二、算法
大家都能取得的一個共識是函數庫對數據類型的選擇對其可重用性起着至關重要的作用。舉例來說,一個求方根的函數,在使用浮點數作爲其參數類型的情況下的可重用性肯定比使用整型作爲它的參數類性要高。而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所起到的幫助微乎甚微,這不光是因爲深入STL需要對C++的高級應用有比較全面的瞭解,更因爲STL的三個部分算法、容器和迭代器三部分是互相牽制或者說是緊密結合的。從概念上講最基礎的部分是迭代器,可是直接學習迭代器會遇到許多抽象枯燥和繁瑣的細節,然而不真正理解迭代器又是無法直接進入另兩部分的學習的(至少對剖析源碼來說是這樣)。可以說,適應STL處理問題的方法是需要花費一定的時間的,但是以此爲代價,STL取得了一種十分可貴的獨立性,它通過迭代器能在儘可能少地知道某種數據結構的情況下完成對這一結構的運算,所以下決心鑽研STL的朋友們千萬不要被一時的困難擊倒。其實STL運用的模式相對統一,只要適應了它,從一個STL工具到另一個工具,都不會有什麼大的變化。
對於STL的使用,也普遍存在着兩種觀點。第一種認爲STL的最大作用在於充當經典的數據結構和算法教材,因爲它的源代碼涉及了許多具體實現方面的問題。第二種則認爲STL的初衷乃是爲了簡化設計,避免重複勞動,提高編程效率,因此應該是“應用至上”的,對於源代碼則不必深究。筆者則認爲分析源代碼和應用並不矛盾,通過分析源代碼也能提高我們對其應用的理解,當然根據具體的目的也可以有不同的側重。
最後要說的是,STL是ANSI/ISO C++標準的一部分,所以對於一個可以有多種C++實現的過程,首先考慮的應該是STL提供的模板(高效且可移植性好),其次纔是各個廠商各自相應的庫(高效但可移植性不好)以及自己去編寫代碼(可移植性好但低效)。
第二部分:
暫且舉幾個非常容易理解的程序源碼:
#include <vector>
using namespace std;
int main(){
vector<int>vi;
int a;
while(true)
{
cout<<"輸入一個整數,按0停止輸入:";
cin>>a;
if(a==0)
break;
vi.push_back(a);
vector<int>::iterator iter;
for(iter=vi.begin();iter!=vi.end();++iter)
cout<<*iter;
}
return 0;
}
#include <string>
#include <vector>
using namespace std;
int main(){
string str="shiyang";
vector <string> vecstr;
vecstr.push_back(str);
vector <string> ::iterator iter= vecstr.begin();
cout<<*iter<<endl;
return 0;
}
#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;
}
#include <list>
#include <numeric>
#include <algorithm>
using namespace std;
//創建一個list容器的實例LISTINT
typedef list<int> LISTINT;
//創建一個list容器的實例LISTCHAR
typedef list<int> LISTCHAR;
int main(){
//--------------------------
//用list容器處理整型數據
//--------------------------
//用LISTINT創建一個名爲listOne的list對象
LISTINT listOne;
//聲明i爲迭代器
LISTINT::iterator i;
//從前面向listOne容器中添加數據
listOne.push_front (2);
listOne.push_front (1);
//從後面向listOne容器中添加數據
listOne.push_back (3);
listOne.push_back (4);
//從前向後顯示listOne中的數據
cout<<"listOne.begin()--- listOne.end():"<<endl;
for (i = listOne.begin(); i != listOne.end(); ++i)
cout << *i << " ";
cout << endl;
//從後向前顯示listOne中的數據
LISTINT::reverse_iterator ir;
cout<<"listOne.rbegin()---listOne.rend():"<<endl;
for (ir =listOne.rbegin(); ir!=listOne.rend();ir++) {
cout << *ir << " ";
}
cout << endl;
//使用STL的accumulate(累加)算法
int result = accumulate(listOne.begin(), listOne.end(),0);
cout<<"Sum="<<result<<endl;
cout<<"------------------"<<endl;
//--------------------------
//用list容器處理字符型數據
//--------------------------
//用LISTCHAR創建一個名爲listOne的list對象
LISTCHAR listTwo;
//聲明i爲迭代器
LISTCHAR::iterator j;
//從前面向listTwo容器中添加數據
listTwo.push_front ('A');
listTwo.push_front ('B');
//從後面向listTwo容器中添加數據
listTwo.push_back ('x');
listTwo.push_back ('y');
//從前向後顯示listTwo中的數據
cout<<"listTwo.begin()---listTwo.end():"<<endl;
for (j = listTwo.begin(); j != listTwo.end(); ++j)
cout << char(*j) << " ";
cout << endl;
//使用STL的max_element算法求listTwo中的最大元素並顯示
j=max_element(listTwo.begin(),listTwo.end());
cout << "The maximum element in listTwo is: "<<char(*j)<<endl;
}
#include <list>
using namespace std;
typedef list<int> INTLIST;
//從前向後顯示list隊列的全部元素
void put_list(INTLIST list, char *name)
{
INTLIST::iterator plist;
cout << "The contents of " << name << " : ";
for(plist = list.begin(); plist != list.end(); plist++)
cout << *plist << " ";
cout<<endl;
}
//測試list容器的功能
int main(){
//list1對象初始爲空
INTLIST list1;
//list2對象最初有10個值爲6的元素
INTLIST list2(10,6);
//list3對象最初有9個值爲6的元素
INTLIST list3(list2.begin(),--list2.end());
//聲明一個名爲i的雙向迭代器
INTLIST::iterator i;
//從前向後顯示各list對象的元素
put_list(list1,"list1");
put_list(list2,"list2");
put_list(list3,"list3");
//從list1序列後面添加兩個元素
list1.push_back(2);
list1.push_back(4);
cout<<"list1.push_back(2) and list1.push_back(4):"<<endl;
put_list(list1,"list1");
//從list1序列前面添加兩個元素
list1.push_front(5);
list1.push_front(7);
cout<<"list1.push_front(5) and list1.push_front(7):"<<endl;
put_list(list1,"list1");
//在list1序列中間插入數據3個9
list1.insert(++list1.begin(),3,9);
cout<<"list1.insert(list1.begin(),3,9):"<<endl;
put_list(list1,"list1");
//測試引用類函數
cout<<"list1.front()="<<list1.front()<<endl;
cout<<"list1.back()="<<list1.back()<<endl;
//從list1序列的前後各移去一個元素
list1.pop_front();
list1.pop_back();
cout<<"list1.pop_front() and list1.pop_back():"<<endl;
put_list(list1,"list1");
//清除list1中的第2個元素
list1.erase(++list1.begin());
cout<<"list1.erase(++list1.begin()):"<<endl;
put_list(list1,"list1");
//對list2賦值並顯示
list2.assign(8,1);
cout<<"list2.assign(8,1):"<<endl;
put_list(list2,"list2");
//顯示序列的狀態信息
cout<<"list1.max_size(): "<<list1.max_size()<<endl;
cout<<"list1.size(): "<<list1.size()<<endl;
cout<<"list1.empty(): "<<list1.empty()<<endl;
//list序列容器的運算
put_list(list1,"list1");
put_list(list3,"list3");
cout<<"list1>list3: "<<(list1>list3)<<endl;
cout<<"list1<list3: "<<(list1<list3)<<endl;
//對list1容器排序
list1.sort();
put_list(list1,"list1");
//結合處理
list1.splice(++list1.begin(), list3);
put_list(list1,"list1");
put_list(list3,"list3");
}