前言
上個週末在和個人同窗爬香山閒聊時,同窗說到STL中的string類曾經讓他備受折磨,幾年前他開發一個系統前對string類還比較清楚,而後隨着程序的複雜度的加深,到了後期,他幾乎對string類失去了信心和信任,他以爲他對string類一頭霧水。老實說,我幾年前也有一樣的痛苦(就是當我寫下《標準C++類string的Copy-On-Write技術》以前的一段時間)。那時,我不得不研究那根本不是給人看的SGI出品的string類的源碼,代碼的可讀性幾乎爲零,並且隨着瞭解越深刻,就越以爲C++的世界中處處都是陷阱和缺陷。愈來愈以爲有時候那些類並不像本身所想象的那樣工做。
爲何會發生這樣的狀況呢?string類只是一個「簡單」的類,若是是一些比較複雜的類呢?而這幾年來,C++陣營聲討標準模板庫中的標準string類愈演愈烈。C++陣營對這個「小子」的爭討就沒有中止過。相信在下一個C++的標準出臺時,string類會有一個大的變化。
瞭解
string
類
在咱們研究string類犯了什麼毛病以前,還讓我先說一下如何瞭解一個C++的類。咱們要了解一個C++的類,通常來講,要從三個方面入手。
1、 意圖(Intention)。知其然還要知因此然,string類的意圖是什麼?只有瞭解了意圖,才知道它的思路。這是瞭解一個事物最重要最根本的部分。否則,你會發現它的行爲並不會像你所指望的那樣。string類的意義有兩個,第一個是爲了處理char類型的數組,並封裝了標準C中的一些字符串處理的函數。而當string類進入了C++標準後,它的第二個意義就是一個容器。這兩件事並不矛盾,咱們要需理解string的機制,須要從這兩個方面考慮。
2、 規格(Specification)。咱們注意到string類有太多的接口函數。這是目前C++陣營中聲討其最重的話題。一個小小的string類竟然有106個成員接口函數。竟然,C++標準委員會會容忍這種「ugly」的事情的發生?目前的認爲致使「C++標準委員會腦子進水」的主流緣由有兩點,一個是爲了提升效率,另外一個是爲了經常使用的操做。
1)讓咱們先來看效率,看看string類中的「==」操做符重載接口:
bool operator==(const string& lhs, const string& rhs);
bool operator==(const string& lhs, const char* rhs);
bool operator==(const char* lhs, const string& rhs);
頭一個很標準,然後兩個彷佛就顯得沒有必要了。若是咱們調用:(Str == 「string」)若是沒有後面兩個接口,string的構造函數會把char*的 」string」轉成string對象,而後再調用第一個接口,也就是 operator==(str, string(「string」))。如此「多餘」的設計只能說是爲了追求效率,爲了省去調用構造/析構函數和分配/釋放內存的時間(這會節省不少的時間)。在後面兩個接口中,直接使用了C的strcmp函數。如此看來,這點設計仍是頗有必要的。string類中有不少爲了追求效率的算法和設計,好比:Copy-on-Write(參看個人《標準C++類string的Copy-On-Write技術》)等。這些東西讓咱們的string變得頗有效率,但也帶來了陷阱。若是不知道這些東西,那麼當你使用它的時候發生不可意料的問題,就會讓你感到迷茫和不知所措。
2)另外一個讓string類擁有這麼龐大的接口的緣由是經常使用的操做。好比string類的substr(),這是一個截取子字符串的函數。其實這個函數並不須要,由於string有一個構造函數能夠從別的string類中指定其起始和長度構造本身,從而實現這一功能。還有就是copy()函數,這也是一個沒有必要的函數,copy這個函數把string類中的內容拷貝到一個內存buffer中,這個方法實踐證實不多有人使用。可能,1)爲了安全起見,須要有這樣一個成員把內容複製出去;2)設計者以爲copy要比strcpy或是memcpy好寫也漂亮不少吧。copy()比起substr()更沒有必要存在。
3、 實現(Implementation)。C++標準並無過多的干預實現。不一樣的產商會有不一樣的實現。不一樣的產商會考慮標準中的一件事情是否符合市場的須要,並要考慮本身的編譯器是否有可以編譯標準的功能。因而,他們老是會輕微或是顛覆地修改着標準。C++在編譯器的差別是使人痛苦和絕望的,若是不瞭解具體的實現,在你使用C++的時候,你也會發現它並不像你所想象的那樣工做。
只有從上述三個方面入手,你才能真正瞭解一個C++類,而你也才能用好C++。C++高手們都是從這樣的三個方面剖析着C++現實中的各類類,並以此來驗證C++類的設計。
String
類犯了什麼錯?
string類其實挺好的。它的設計頗有技術含量。它有高的效率、運行速度快、容易使用。它有很充足的接口能夠知足各式各樣的法,使用起來也很靈活。
然而,這個string相似乎有點沒有與時俱進,它如今的設計還保持着10年之前的樣子,10年來,整個技術環境都出現不少變革,人們也在使用C++的過程當中獲得了許許多的經驗。好比:如今的幾乎全部的程序都運行在一個多進/線程的環境中,而10年前主流還只是單進/線程的應用,這是一個巨大的變化。近幾年來,C++陣營也在實踐中取得了不少的經驗,特別是模板技術,而咱們的STL顯然沒能跟上腳步。
首當其衝的是string類,目前C++陣營對string類的聲討主要集中在下面幾個方面。對於下面的這些問題,C++陣營仍是爭論不休。不過,做爲一個好的程序員,咱們應該在咱們的設計和編程中注意一下這些方面。
1)目前的標string類有106個接口函數(包括構造和析構函數),若是考慮上默認參數,那麼就一共有134不一樣的接口。其中有5個函數模板還會產生無窮多個各類各樣的函數。還有各類各樣的性能上的優化。在這麼從多的成員函數中,不少都是冗餘沒必要要的。最糟糕的是,衆多程序員們並不瞭解它們,導到要麼浪費了它的優點,要麼踩中了其中的陷阱。
2)不少人認爲string類提供的功能中,該有的沒有,已有的又很冗餘。string類在同一個功能上實現了屢次,而有一些功能卻沒有實現。如:大小寫不區分的比較,寬行字符(w_char)的支持,和字符char型數據的接口等等。
3)做爲一個STL的容器,string類和的設計和其它容器基本同樣。這些STL都不鼓勵被繼承。由於STL容器的設計者們的幾乎都遺忘了虛函數的使用,這樣阻止了多態性,也許,這也是一個爲了考慮效率和性能的設計。對於STL容易這樣的設計,大多數人表示接受。但對於string類,不少人認爲,string類是一個特殊的類,考慮到它被使用的頻率,string類應該獲得特殊的照顧。
4)還有不少人認爲標準的strng類強行進行動態內存分配(malloc),那怕是一個很短的字符串。這會致使內存碎片問題(咱們知道內存碎片問題會讓malloc很長時間才能返回,因爲下降了整個程序的性能。而關於內存碎片問題,這是一個很嚴重的問題,目前除了重啓應用程序,尚未什麼好的方法)。他們認爲,string類的設計加重了內存碎片問題。他們但願string類可以擁有本身的棧上內存來存放一些短字符串,而不須要老是去堆上分配內存。(由於用於string類的字符串長度幾乎都不會很長)
5)不少string類的實現,都採用了Copy-On-Write(COW)技術。雖然這是一個頗有效率的技術。可是由於內存的共享,致使了程序在「多線程」環境中及容易發生錯誤,若是分別在兩個線程中的string實例共享着同一塊內存,頗有可能發生潛在的內存問題(參看個人《標準C++類string的Copy-On-Write技術》最後一節示例)。目前這一技術頗有可能從下一版本的標準中移去。(而一些新版本的STL都不支持COW了,如Microsoft VC8.0下的STL)
6)標準的string類不支持policy-base技術的錯誤處理。string遇到錯誤時,只是簡單地拋出異常。雖然這是一個標準,但有一些狀況下不會使用異常(GCC –fno-exception)。另外,不可能要求全部的程序都要在使用string操做的時候try catch,一個比較好的方法是string類封裝有本身的error-handling函數,而且可讓用戶來定義須要使用哪種錯誤處理機制。
因爲string類的的種種不如人意,特別是106個接口函數讓許多人難以接受,有不少人都在寫適合本身的string類。但整體來講,標準的string類是一個好壞難辨的類。不管你是不是一個高級諮深的程序員,你都會用到它。它和整個C++同樣,都是一把雙刃劍,在大多數狀況下,它仍是值得咱們信賴。
目前,對string類的爭論有不少不少。C++標準委員會也說:「The C++ Standard is the best we could make it. If we could have agreed on how to make it better, then we would have made it better.」在C++這麼一個混亂的領地,雖然STL並不完美,但對比起來講,他仍是顯得那麼地漂亮和精緻辭。
後記
又到了關於C++文章結束的時候,我仍是要老調重彈。C++這個世界是很複雜很危險的。單單本文章中的一個小小的string類就能引發這麼多的討論,況且是別的類?
讓咱們大膽地懷疑一下, C++是否真是一種高級的語言?目前的C++須要使用他的人有至關的專業技術水平,而寄但願於人的高水平看來是和技術的發展背道而馳的。好的一門開發語言是讓人能夠容易方便地把更多的精力集中在業務邏輯性上。而C++這門語言卻須要技術人員比以住的C有着更爲高深和專業的技術知識和能力。
是咱們對string的「無知」搞亂了咱們的程序,仍是string這個類的設計把咱們的程序搞亂了?是C++本身把軟件開發搞得一團混亂?仍是使用C++的人把其搞得一團混亂?好像兼而有之,目前咱們沒法定論,但咱們知道,不管是C++標準委員會,仍是開發人員,你們的C++將來之路都還有很長。