原文地址:https://blog.csdn.net/bamboolsu/article/details/46453705
一侵贵, 定義
STL = Standard Template Library堪遂,標準模板庫
標準模板庫,惠普實驗室開發(fā)的一系列軟件的統(tǒng)稱本缠。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普實驗室工作時所開發(fā)出來的入问。這可能是一個歷史上最令人興奮的工具的最無聊的術(shù)語丹锹。從根本上說,STL是一些“容器”的集合芬失,這些“容器”有l(wèi)ist,vector,set,map等楣黍,STL也是算法和其他一些組件的集合。這里的“容器”和算法的集合指的是世界上很多聰明人很多年的杰作棱烂。STL的目的是標準化組件锡凝,這樣就不用重新開發(fā),可以使用現(xiàn)成的組件垢啼。STL現(xiàn)在是C++的一部分,因此不用額外安裝什么张肾。
STL被內(nèi)建在你的編譯系統(tǒng)之內(nèi)芭析。STL的版本很多,常見的有HP STL吞瞪、PJ STL馁启、 SGI STL等。
在C++標準中芍秆,STL被組織為下面的13個頭文件:<algorithm>惯疙、<deque>、<functional>妖啥、<iterator>霉颠、<array>、<vector>荆虱、<list>蒿偎、<forward_list>朽们、<map>、<unordered_map>诉位、<memory>骑脱、<numeric>、<queue>苍糠、<set>叁丧、<unordered_set>、<stack>和<utility>岳瞭。
組成部分
STL可分為容器(containers)拥娄、迭代器(iterators)、空間配置器(allocator)寝优、配接器(adapters)条舔、算法(algorithms)、仿函數(shù)(functors)六個部分乏矾。
STL容器就為我們提供了這樣的方便孟抗,它允許我們重復利用已有的實現(xiàn)構(gòu)造自己的特定類型下的數(shù)據(jù)結(jié)構(gòu),通過設置一些模板類钻心,STL容器對最常用的數(shù)據(jù)結(jié)構(gòu)提供了支持凄硼,這些模板的參數(shù)允許我們指定容器中元素的數(shù)據(jù)類型,可以將我們許多重復而乏味的工作簡化捷沸。
容器部分主要由頭文件<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>組成摊沉。對于常用的一些容器和容器適配器(可以看作由其它容器實現(xiàn)的容器),可以通過下表總結(jié)一下它們和相應頭文件的對應關(guān)系痒给。
序列式容器
向量(vector) 連續(xù)存儲的元素<vector>
列表(list) 由節(jié)點組成的雙向鏈表说墨,每個結(jié)點包含著一個元素<list>
雙端隊列(deque) 連續(xù)存儲的指向不同元素的指針所組成的數(shù)組<deque>
容器適配器
棧(stack) 后進先出的值的排列 <stack>
隊列(queue) 先進先出的值的排列 <queue>
優(yōu)先隊列(priority_queue) 元素的次序是由作用于所存儲的值對上的某種謂詞決定的的一種隊列 <queue>
關(guān)聯(lián)式容器
集合(set) 由節(jié)點組成的紅黑樹,每個節(jié)點都包含著一個元素苍柏,節(jié)點之間以某種作用于元素對的謂詞排列尼斧,沒有兩個不同的元素能夠擁有相同的次序 <set>
多重集合(multiset) 允許存在兩個次序相等的元素的集合 <set>
映射(map) 由{鍵,值}對組成的集合试吁,以某種作用于鍵對上的謂詞排列 <map>
多重映射(multimap) 允許鍵對有相等的次序的映射 <map>
迭代器從作用上來說是最基本的部分棺棵,可是理解起來比前兩者都要費力一些(至少筆者是這樣)。軟件設計有一個基本原則熄捍,所有的問題都可以通過引進一個間接層來簡化烛恤,這種簡化在STL中就是用迭代器來完成的。概括來說余耽,迭代器在STL中用來將算法和容器聯(lián)系起來缚柏,起著一種黏和劑的作用。幾乎STL提供的所有算法都是通過迭代器存取元素序列進行工作的宾添,每一個容器都定義了其本身所專有的迭代器船惨,用以存取容器中的元素柜裸。
迭代器部分主要由頭文件<utility>,<iterator>和<memory>組成。<utility>是一個很小的頭文件粱锐,它包括了貫穿使用在STL中的幾個模板的聲明疙挺,<iterator>中提供了迭代器使用的許多方法,而對于<memory>的描述則十分的困難怜浅,它以不同尋常的方式為容器中的元素分配存儲空間铐然,同時也為某些算法執(zhí)行期間產(chǎn)生的臨時對象提供機制,<memory>中的主要部分是模板類allocator,它負責產(chǎn)生所有容器中的默認分配器恶座。
STL提供了大約100個實現(xiàn)算法的模版函數(shù)搀暑,比如算法for_each將為指定序列中的每一個元素調(diào)用指定的函數(shù),stable_sort以你所指定的規(guī)則對序列進行穩(wěn)定性排序等等跨琳。這樣一來自点,只要我們熟悉了STL之后,許多代碼可以被大大的化簡脉让,只需要通過調(diào)用一兩個算法模板桂敛,就可以完成所需要的功能并大大地提升效率。
算法部分主要由頭文件<algorithm>溅潜,<numeric>和<functional>組成术唬。<algorithm>是所有STL頭文件中最大的一個(盡管它很好理解),它是由一大堆模版函數(shù)組成的滚澜,可以認為每個函數(shù)在很大程度上都是獨立的粗仓,其中常用到的功能范圍涉及到比較、交換设捐、查找借浊、遍歷操作、復制萝招、修改巴碗、移除、反轉(zhuǎn)即寒、排序、合并等等召噩。<numeric>體積很小母赵,只包括幾個在序列上面進行簡單數(shù)學運算的模板函數(shù),包括加法和乘法在序列上的一些操作具滴。<functional>中則定義了一些模板類凹嘲,用以聲明函數(shù)對象
二, 為何某些公司不允許使用C++ STL构韵?
聽朋友說他們公司不允許使用 C++ STL周蹭。有點不懂趋艘,為何不允許使用呢?難道出于安全凶朗、效率瓷胧?不應該啊,當時急著沒細問棚愤,大家能解釋一下嗎搓萧?不知道 Boost 如何?
說幾個STL的缺點吧宛畦,雖然都是在比較極端的情況下出現(xiàn)瘸洛,但是對于一些大項目還是會遇到的
1. 代碼膨脹問題
每一個實例化過的模板類,都會膨脹出一份獨立的代碼次和,比如
std::vector<std::string>, std::vector<int>反肋,編譯后會產(chǎn)生兩份代碼,在VC2008下踏施,每份代碼大約是3-4kb石蔗,這是因為vector比較簡單代碼少,如果是map則會產(chǎn)生30-50kb的代碼读规,因為map里有個復雜的紅黑樹抓督。對于數(shù)據(jù)處理類的代碼里一般會定義很多種不同的結(jié)構(gòu)體,不同的結(jié)構(gòu)體放到不同的容器里束亏,就會實例化出很多個類的代碼铃在,我見過一個項目里,這樣的vector就有數(shù)百個碍遍。
2. 內(nèi)存使用效率問題 (以vc++2008為例)
stl在內(nèi)存使用效率上是比較低效的定铜,比如std::string,它的sizeof大概是28怕敬,因為它有一個內(nèi)置的16字節(jié)數(shù)組揣炕,用來做小字符串優(yōu)化的,就是說低于16字節(jié)的字符串都會至少占用28字節(jié)內(nèi)存东跪,如果剛好17字節(jié)字符串畸陡,則會占用28字節(jié)+額外分配的字符串內(nèi)存,額外分配的內(nèi)存是一個堆塊虽填,又有很多浪費丁恭,相比用一個char *存儲字符串大約多占用了一倍內(nèi)存。
還有map<>斋日,每一個map的node都是一塊獨立分配的內(nèi)存牲览,如果是 map<int, int>呢,那就很悲劇了恶守,為了存一個int要消耗幾十個字節(jié)第献,很浪費的贡必。
如果元素數(shù)量有百萬級,那么內(nèi)存占用就很可觀了庸毫,這種情況下建議自己實現(xiàn)allocator仔拟,做內(nèi)存池。
3. deep copy問題
讓兩個容器的實例做賦值操作岔绸,看起來就一條語句理逊,實際上容器里的每個元素都執(zhí)行了一次賦值操作。如果容器里有百萬級的數(shù)據(jù)盒揉,那么一個等號就產(chǎn)生了幾百萬次的構(gòu)造和析構(gòu)晋被。
傳遞參數(shù)的時候一定要用 const 引用,賦值可以用 swap代替刚盈。
4. 隱式類型轉(zhuǎn)換
比如 有個函數(shù)
void doSomething(const std::string &str);
調(diào)用的時候
doSomething("hello");
能編譯執(zhí)行羡洛,但是會產(chǎn)生一個臨時的匿名的std::string實例,把"hello"復制一遍藕漱,然后在調(diào)用完成后析構(gòu)掉欲侮。如果這個發(fā)生在循環(huán)體內(nèi)部有可能影響性能。
以上這些問題肋联,在小程序里或者數(shù)據(jù)規(guī)模不大的時候威蕉,比如容器內(nèi)元素只有幾千這個規(guī)模,都不是什么大問題橄仍,那時開發(fā)效率才是重點韧涨,但是一旦有大數(shù)據(jù)stl容器會成為性能瓶頸的。
我并不是主張不用STL侮繁,而是要充分了解STL的優(yōu)缺點虑粥,根據(jù)應用場景做選擇。
1宪哩,性能
隨著C++11標準在各大編譯器的實現(xiàn)娩贷,有了move和rvalue,STL的性能不會是瓶頸锁孟,而且另一方面彬祖,你的程序既然要最高的性能,為什么要選擇C++呢品抽?你還是看重C++的抽象能力(相對于C)對吧涧至?既然你都看重的不僅僅是性能了,那還拿什么性能說事兒桑包?
當然,你也可以自己去實現(xiàn)一套STL的東西(比如EA就自己搞過一套EA STL)纺非,但誰來負責進化呢哑了?你要做很多benchmark赘方,寫很多test case,還要在項目成員間宣傳“用了我的STL弱左,手不疼了窄陡,腳不抽經(jīng)了。拆火。跳夭。”们镜,不然你離職后币叹,這坨東西自會被后人慢慢當一坨東西一樣扔掉。
有這時間模狭,少百分之幾的性能又怎樣颈抚,陪陪老婆孩子不挺好的嗎?
2嚼鹉,對STL不熟悉
如樓上已經(jīng)有幾位知友提到贩汉,Server端的程序員大多是從C轉(zhuǎn)過來的,對C++的一些高級特性(包括STL的實現(xiàn))并沒有很好的掌控能力锚赤,自然會選擇用C的方式來用C++匹舞。
3,調(diào)試
如果不用STL就好調(diào)試了线脚,那為什么還有那么多關(guān)于調(diào)試的書籍赐稽?而且還賣的不錯。
三酒贬, 理解C++之std 與 stl
1,首先明確 std是一個 命名空間的名字又憨。
2,其次锭吨,明確STL是 Standard Template Library的縮寫蠢莺,即標準模板庫。
3零如,2者關(guān)系: In fact, all identifiers of the C++ standard library are defined in a namespace called std. 而STL被容納與C++ Standard Library里躏将。即2者均屬于 C++ Standard Library里。STL是其中的一部分內(nèi)容考蕾,std是作為一個外部的名字祸憋。
4,舉例 C++一般的庫函數(shù) 也要#include <Iosstream> std::cout肖卧,感覺更多的是庫函數(shù)蚯窥。
C++之STL里的函數(shù) 用個#include <vector>,感覺更多的是容器。
四拦赠, c++0x c++11 幾個特性:
原生支持正則表達式巍沙?
各種匿名函數(shù)?
還有 auto 的一種使用是根據(jù)上下文推斷數(shù)據(jù)類型荷鼠?
五句携, c++11 STL, C++11 中STL庫中新增內(nèi)容
C++ 11一個比較顯著的變化是以前boost庫中的一些函數(shù)被正式標準化合入到STL中了,本文就簡單的介紹一下允乐。
引用包裝器(Reference Wrapper)
當模板函數(shù)參數(shù)為泛型類型的時候矮嫉,無法推導出是傳值還是傳引用,默認情況下會使用傳值的方式牍疏。這是我們可以用std::ref顯式指定以傳引用的方式實例化模板函數(shù)蠢笋。
#include <functional>
#include <iostream>
template <class T>
void foo(T arg)
{
arg++;
}
int main()
{
int count = 3;
foo(count); //此時傳的是值,模板實例化為foo(int)麸澜,count值不變 std::cout << count << std::endl;
foo(std::ref(count)); //此時傳的是引用挺尿,模板實例化為foo(int&),count值加1
std::cout << count << std::endl;
}
智能指針(Smart Pointers)
智能指針主要引入了shared_ptr炊邦、weak_ptr编矾、unique_ptr三種,其中shared_ptr和weak_ptr就是boost庫中的應對象馁害,我以前也寫過相關(guān)文章介紹他們窄俏,這里就不介紹了。
而新引入的unique_ptr和以前介紹過的boost庫中的scoped_ptr比較類似碘菜,而STL中本身就有一個類似的auto_ptr凹蜈,它們之間的主要區(qū)別是:
auto_ptr可以支持'='操作,也可作為函數(shù)的返回值
unique_ptr不支持'='操作忍啸,可以作為函數(shù)的返回值
scoped_ptr即不支持'='操作仰坦,也不能作為函數(shù)的返回值
相比較而言,unique_ptr權(quán)限比auto_ptr小计雌,也沒有scope_ptr不能作為返回值的限制悄晃,是用起來最合適的,完全可以代替auto_ptr
仿函數(shù)
四個boost庫也合入了stl庫中:
其中function和bind我以前在介紹boost庫中已經(jīng)介紹過凿滤,在支持auto關(guān)鍵字后妈橄,通過bind創(chuàng)建function更加簡單了,我們只需要用一句話就能創(chuàng)建成員函數(shù)的仿函數(shù)翁脆。
#include <iostream>
#include <functional>
using namespace std;
usingnamespacestd::placeholders; struct X
{
bool foo (int a) { cout<< a << endl; return false;}
};
int main()
{
X x;autofunc = bind(&X::foo, &x, _1); func(5);
}
PS:使用bind的時候需要加入std::placeholders的using眷蚓,否則編譯報語法錯誤。
不過感覺bind基本上被lambda表達式給秒了反番,對于上面例子里沙热,用lambda表達式的寫法如下:
auto func = [&x](int a) { x.foo(a); };
function<void (int)> func = [&x](int a) { x.foo(a); };
由于lambda表達式是語法糖叉钥,因此可讀性方面更好(感覺簡潔度都快接近C#的匿名函數(shù)了),沒有_1篙贸,_2之類的占位符沼侣,對于函數(shù)的調(diào)用方式也是顯式直接調(diào)用,更加直觀歉秫。
而result_of在auto引入后感覺也基本上沒有用了,直接使用auto要簡單得多养铸。
容器
容器方面主要加了如下幾個:
tuple
array
unordered_set和unordered_map
其中tuple和array基本上就是boost相關(guān)庫給標準化了雁芙,而unordered_set和unordered_map則是hash表方式的set和map,以提供更高的查詢性能钞螟。使用方式和原來二叉樹版的也大同小異兔甘,這里就不多介紹了。
正則表達式
Boost的regex庫也終于標準化了鳞滨,要使用字符串處理的可以不用到處找第三方的正則表達式庫了洞焙。不過目前VC還不支持像C#那樣取消轉(zhuǎn)義字符(gcc可以),在代碼里面的正則表達式依然非常難讀拯啦,希望MS能盡快把raw string literal給支持上澡匪。
線程
Boost的線程庫也標準化了,另外那個類似于.Net TPL庫的packaged_task也標準化了褒链,由于它有不少可以介紹的地方唁情,我會專門寫篇文章來介紹它,這里就不多說了甫匹。
時間函數(shù)
其實C語言標準庫是提供了時間函數(shù)的甸鸟,不過極度難用,現(xiàn)在Boost的時間函數(shù)chrono已經(jīng)給標準化了兵迅,雖然還是比不上.Net的TimeSpan好用抢韭,但起碼比標準C的那套好太多了。
#include <iostream>
#include <chrono>
#include <ctime>
using namespace std;
int fibonacci(int n)
{
if (n < 3) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
int main()
{
auto start = chrono::system_clock::now();
int result = fibonacci(40);
auto end = chrono::system_clock::now();
int elapsed_seconds = chrono::duration_cast<chrono::milliseconds>
(end-start).count();
auto end_time = chrono::system_clock::to_time_t(end);
std::cout << "result: " << result << endl
<< "finished computation at " << std::ctime(&end_time)
<< "elapsed time: " << elapsed_seconds << "ms\n";
}
另外一個日期函數(shù)Boost.Date好像還沒有標準化恍箭,要用到日期相關(guān)功能還是只能用boost庫刻恭。
六, boost
Boost庫是一個可移植季惯、提供源代碼的C++庫吠各,作為標準庫的后備,是C++標準化進程的開發(fā)引擎之一勉抓。 Boost庫由C++標準委員會庫工作組成員發(fā)起贾漏,其中有些內(nèi)容有望成為下一代C++標準庫內(nèi)容。在C++社區(qū)中影響甚大藕筋,是不折不扣的“準”標準庫纵散。Boost由于其對跨平臺的強調(diào),對標準C++的強調(diào),與編寫平臺無關(guān)伍掀。大部分boost庫功能的使用只需包括相應頭文件即可掰茶,少數(shù)(如正則表達式庫,文件系統(tǒng)庫等)需要鏈接庫蜜笤。但Boost中也有很多是實驗性質(zhì)的東西濒蒋,在實際的開發(fā)中實用需要謹慎。
Boost庫是為C++語言標準庫提供擴展的一些C++程序庫的總稱把兔。
Boost庫由Boost社區(qū)組織開發(fā)沪伙、維護。其目的是為C++程序員提供免費县好、同行審查的围橡、可移植的程序庫。Boost庫可以與C++標準庫完美共同工作缕贡,并且為其提供擴展功能翁授。Boost庫使用Boost License來授權(quán)使用。
Boost社區(qū)建立的初衷之一就是為C++的標準化工作提供可供參考的實現(xiàn)晾咪,Boost社區(qū)的發(fā)起人Dawes本人就是C++標準委員會的成員之一收擦。在Boost庫的開發(fā)中,Boost社區(qū)也在這個方向上取得了豐碩的成果禀酱。在送審的C++標準庫TR1中炬守,有十個Boost庫成為標準庫的候選方案。在更新的TR2中剂跟,有更多的Boost庫被加入到其中减途。從某種意義上來講,Boost庫成為具有實踐意義的準標準庫曹洽。
可下載Boost C++ Libraries安裝boost庫鳍置。大部分boost庫功能的使用只需包括相應頭文件即可,少數(shù)(如正則表達式庫送淆,文件系統(tǒng)庫等)需要鏈接庫税产。里面有許多具有工業(yè)強度的庫,如graph庫偷崩。