新部落格位址:http://gorthon.sinaapp.com/
作者:施楊(施楊's Think out)
出處:http://shiyangxt.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。
學無止境!!!
第一部分:(參考百度百科)
一、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的初衷乃是為了簡化設計,避免重複勞動,提高程式設計效率,是以應該是“應用至上”的,對于源代碼則不必深究。筆者則認為分析源代碼和應用并不沖突,通過分析源代碼也能提高我們對其應用的了解,當然根據具體的目的也可以有不同的側重。
第二部分:
暫且舉幾個非常容易了解的程式源碼:
#include < iostream >
#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 < iostream >
#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 < 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 ;
}
#include < iostream >
#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 < iostream >
#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 " );
}
第三部分:
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.請把書上的程式例子親手輸入到電腦上實踐,即使配套CD光牒中有源代碼;
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> 通用類型數學宏
作者:施楊(施楊's Think out)
出處:http://shiyangxt.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。
分