讓自己習慣C++
條款01:視C++為一個語言聯邦
C++可視為:
- C:以C為基礎。
- 面向對象的C++:添加面向對象特性。
- 模板C++:泛型編程概念,使用模板。
- STL:使用STL的容器、迭代器、算法、及函數對象。
四者的集合。
條款02:盡量以const、enum、inline替換 #define
對于單純常量,盡量以const對象或enums枚舉來代替#define。
對于函數宏,用inline函數代替#define(define是死板的替換,容易產生傳遞計算式類似累加多次的問題)
條款03:盡可能使用const
聲明const可以幫助編譯器偵測錯誤用法,避免改變不應改變的對象、參數、返回類型等。
編譯器對const是“像素級”的不變檢查,但編程時應該以“邏輯級”的不變思路來做,對于一些可能變化的變量,使用mutable修飾讓編譯器允許其變化。
由于函數有重載特性,當const和non-const成員函數有實質等價的實現時,用non-const版本調用const版本來避免代碼重復,但不要反過來調用,這不符合邏輯。
條款04:確定對象被使用前已先被初始化
確定對象在使用前已經初始化,避免一些難以預測的問題。
為內置類型手動做初始化,C++不保證初始化它們。
構造函數使用成員初始化列表來賦值,而不是在構造函數里去賦值(會導致賦值兩次,浪費了),列表的排列次序保持和class中聲明次序一致。
對于一些可能在被別的類直接調用其成員函數、值的類,最好改為暴露一個返回其類對象的引用的函數的形式,而不是暴露其類對象本身,這可以保證在函數內完成初始化,避免被調用時還沒有初始化。
構造/析構/賦值運算
條款05:了解C++默默編寫并調用哪些函數
當沒有聲明時,編譯器會自動為類創建默認構造函數、析構函數、復制構造函數和賦值構造函數,但如果成員變量中包含引用、const這些不能被改變的值,則不會去生成賦值構造函數,因為無法修改引用對象和const的值,除非我們自己去定義賦值構造函數的行為。
條款06:若不想使用編譯器自動生成的函數,就該明確拒絕
若不想使用編譯器自動生成的函數,可將相應的成員函數申明為private并且不予實現?;蛘呃^承一個類似uncopyable的基類,該基類的相應函數為private且不予實現,這樣子類調用時會去調用基類的該函數,從而被編譯器拒絕。
條款07:為多態基類聲明虛析構函數
如果一個基類可能有多態子類,那么就該聲明一個虛析構函數。
如果一個類有任何虛函數,那么它就應該有虛析構函數。
如果一個類不被用來做基類,那么就不該聲明虛析構函數。
條款08:別讓異常逃離析構函數
析構函數不要拋出異常,如果析構函數中調用的函數可能拋出異常,析構函數應該捕捉并記錄下來然后吞掉他(不傳播)或結束程序。同時最好提供一個普通函數用來供用戶執行可能異常的該操作。
條款09:絕不在構造和析構過程中調用虛函數
在構造函數和析構函數中不要去調用虛函數,因為子類在構造/析構時,會調用父類的構造/析構函數,此時其中的虛函數是調用父類的實現,但這是父類的虛函數可能是純虛函數,即使不是,也可能不符合你想要的目的(是父類的結果不是子類的結果)。
如果想調用父類的構造函數來做一些事情,替換做法是:在子類調用父類構造函數時,向上傳遞一個值給父類的構造函數。
條款10:令 operator= 返回一個*this 引用
賦值操作符要反回一個 *this 的引用,如:
TheClass& operator=(const TheClass& rhs) {
...
return *this;
}
條款11:在 operator= 中處理“自我賦值”
由于變量有別名的存在(多個指針或引用只想一個對象),所以可能出現自我賦值的情況。比如 a[i] = a[j],可能是同一個對象賦值。這時就需要慎重處理賦值操作符以免刪除了自己后再用自己來賦值。
解決方法有:
- 先進行對象是否相同的檢查。
- 先創建一個temp對象指向本對象,然后令本對象復制目標對象,然后刪除temp對象(原本對象)。
- 先創建一個temp對象復制目標對象,然后交換temp對象與本對象。
條款12:復制對象時勿忘其每一個成分
復制構造函數和賦值構造函數要確保復制了對象內的所有成員變量和所有基類成分,這意味著你如果自定義以上構造函數,那么每增加成員變量,都要同步修改以上構造函數,且要調用基類的相應構造函數。
復制構造函數和賦值構造函數看似代碼類似,但不要用一個調用另一個,好的做法是建立一個private的成員函數來做這件事,然后兩個構造函數都調用該成員函數。
資源管理
條款13:以對象管理資源
為了確保一個對象在初始化后能夠最終有效被delete,最好使用shared_ptr和auto_ptr,而前者更好,因為是基于引用計數機制,可以在復制時保持兩個指針都指向同一對象,且只有兩個指針都銷毀時才delete,而auto_ptr只會保證一個指針有效,在復制時,原指針會指向null。
對于數組對象,兩個指針不會使用對應的delete[],所以容易發生錯誤,最好使用string或vector來取代數組。
條款14:在資源管理類中小心copying行為
如果對想要自行管理delete(或其他類似行為如上鎖/解鎖)的類處理復制問題,有以下方案,先創建自己的資源管理類,然后可選擇:
- 禁止復制,使用條款6的方法
- 對復制的資源做引用計數(聲明為shared_ptr),shared_ptr支持初始化時自定義刪除函數(auto_ptr不支持,總是執行delete)
- 做真正的深復制
- 轉移資源的擁有權,類似auto_ptr,只保持新對象擁有。
條款15:在資源管理類中提供對原始資源的訪問
封裝了資源管理類后,API有時候往往會要求直接使用其原始資源(作為參數的類型只能接受原始資源,不接受管理類指針),這時候就需要提供一個獲取其原始資源的方法。有顯式轉換方法(如指針的->和(*)操作,也比如自制一個getXXX()函數),還有隱式轉換方法(比如覆寫XXX()取值函數)。顯式操作比較安全,隱式操作比較方便(但容易被誤用)。
條款16:成對使用new和delete時要采取相同形式
new 對應 delete。
new a[4] 對應 delete [] a。
兩者的使用必須對應。對于數組,不建議使用typedef行為,這會讓使用者不記得去delete []。對于這種情況,建議使用string或者vector。
條款17:以獨立語句將newed對象置入智能指針
如果有函數參數接收智能指針對象,那么該智能指針對象一定要在調用該函數前用獨立語句去創建,否則在創建所指對象和用該對象綁定智能指針兩個操作之間,可能插入一些操作(由于C++的獨特性),這時候如果出異常,那么會造成創建的對象還沒來得及用智能指針修飾,也就無法被自動回收了。
設計與聲明
條款18:讓接口容易被正確使用,不易被誤用
好的接口要容易被正確使用,不容易被誤用,符合客戶的直覺。
- 促進正確使用的辦法包括保持接口的一致性,既包括自定義接口之間的一致性,也包括與內置類型行為的相似一致性。
- 阻止誤用的辦法包括建立新類型來限制該類型上的操作、束縛對象的值以及消除客戶管理資源的責任,以此來作為接口的參數與返回類型。
- shared_ptr支持定制刪除函數,所以可以很方便的實現上述問題,以及防范DLL問題。
條款19:設計class猶如設計type
在設計class時,要考慮一系列的問題,包括
- 對象的創建和銷毀(構造、析構)
- 對象的初始化與賦值(構造、賦值操作符)
- 復制操作(復制構造)
- 合法值(約束條件)
- 繼承體系(注意虛函數)
- 支持的類型轉換(顯示轉換、類型轉換操作符)
- 成員函數和成員變量的可見范圍(public/protected/private)
- 是否用模板就能實現?
條款20:寧以傳遞const引用替換傳遞值
盡量用 常量引用類型 來作為函數的參數類型,這通常比較高效,也可以解決基類參數類型被賦值子類時引起的內容切割問題。
但對于內置類型和STL的迭代器與函數對象,通常編譯器會對其專門優化,直接傳值類型往往比較恰當。
條款21:必須返回對象時,別妄想返回其引用
雖然函數參數最好用引用值,但函數返回值卻不要隨便去用引用,這回造成很多問題,比如引用的對象在函數結束后即被銷毀,或是需要付出很多成本和代碼來保證其不被銷毀且不重復,這大概率沒有必要,就返回一個值/對象就好了。
條款22:將成員變量聲明為private
切記將成員變量聲明為private,這可以保證客戶訪問數據的一致性、可以細微劃分訪問控制、允許約束條件獲得保證,并提供類作者充分的實現彈性來修改對其的處理,因為這保證了“封裝性”,作者可以改變實現和對成員變量的操作,而不改變客戶的調用方式。
protected并不比public更加具有封裝性,因為protected修飾的成員變量一旦修改,也會造成子類的大量修改。
條款23:寧以非成員、非友元替換成員函數
寧可拿非成員非友元函數來替換成員函數。因為這種函數位于函數之外,不能訪問類的private成員變量和函數,保證了封裝性(沒有增加可以看到內部數據的函數量),此外,這些函數只要位于同一個命名空間內,就可以被拆分為多個不同的頭文件,客戶可以按需引入頭文件來獲得這些函數,而類是無法拆分的(子類繼承與此需求不同),因此這種做法有更好的擴充性。
條款24:若所有參數皆需類型轉換,請為此采用非成員函數
如果你要為某個函數的所有參數(包括this所指對象本身)進行類型轉換,那么該函數必須是個非成員函數。
舉個例子,你想為一個有理數類實現乘法函數,支持與int類型的乘積,可以,因為傳參int進去后會調用構造函數隱式轉換為有理數類型,同時你想滿足交換律,這時就會報錯,因為int類型并沒有一個函數用來支持你的有理數類做參數的乘法運算。解決方案是將該乘法運算函數作為一個非成員函數,傳兩個參數進去,這樣不管你的int放在前面還是后面,都能作為參數被轉換類型了。
但是,非成員函數不代表就一定成為友元函數,能夠通過public函數調用完成功能的,就不該設為友元函數,避免權力過大造成麻煩。
條款25:考慮寫出一個不拋異常的swap函數
由于swap函數如此重要,需要特別對他做出一些優化。
常規的swap是簡單全復制三次對象進行交換(包括temp對象),如果效率足夠就用常規版。
如果效率不夠,那么給你的類提供一個成員函數swap,用來對那些復制效率低的成員變量(通常是指針)做交換。
然后,提供一個非成員函數的swap來調用這個成員函數,供別人調用置換。
對于類(非模板),為標準std::swap提供一個特定版本(swap是模板函數,可以特化)。
在使用swap時,記得 using std::swap,讓編譯器可以獲取到標準swap或特化版本。編譯器會自行從所有可能性中選擇最優版本。
實現
條款26:盡可能延后變量定義式的出現時間
盡可能延后變量定義式的出現,既包括延后構造它,保證只有真正使用才構造;也包括只有到賦值時才構造它,避免默認構造函數無畏調用。
對于循環操作,在循環前還是中進行構造,取決于賦值操作與構造+析構操作的成本對比。
- 循環前:1個構造函數+1個析構函數+n個賦值操作
- 循環后:n個構造函數+n個析構函數
條款27:盡量少做轉型操作
盡量避免使用轉型cast(包括C的類型轉換和C++的四個新式轉換函數),特別是注重效率的代碼中避免用dynamic_casts。如果一定要用,試著考慮無需轉型的替代設計,例如為基類添加一個什么也不做的衍生類使用的函數,避免在使用時需要將基類指針轉型為子類指針。
如果一定要轉型,試著將其隱藏于某個函數后,客戶調用該函數而無需自己用轉型。
寧可使用C++新式轉型,也不用用C的舊式,因為新式的更容易被注意到,而且各自用途專一。
條款28:避免返回handles指向對象內部成分
避免讓外部可見的成員函數返回handles(包括引用、指針、迭代器)指向對象內部(更隱私的成員變量或函數),即使返回const修飾也有風險。這一方面降低了封裝性,另一方面可能導致其指向的對象內部元素被修改或銷毀。
條款29:為異常安全而努力是值得的
異常安全函數是指即使發生異常也不會泄露資源或者導致數據結構破壞,分三種保證程度:基本保證、強烈保證和不拋異常型。
只有基本類型才確保了不拋異常型。對于我們自己設計的函數,往往想要提供強烈保證,即一旦發生異常,程序的整個狀態會回到執行函數前的狀態,實現方法一般用復制一個副本然后執行操作,全部成功后再替換原對象的方式來實現。但這一操作有時對時間和空間的消耗較大,適用性不強。這種情況下可以提供基本保證。
函數提供的保證程度通常最高只等于其所調用的各個函數中的保證的最弱者——木桶理論。
條款30:透徹了解inline的里里外外
只將inline用在小型、被頻繁調用的函數身上。inline會帶來體積增大的問題,此外,不要對構造函數、析構函數等使用inline,即使你自己在其中寫的代碼可能很少,編譯器卻會為他添加很多代碼。
不要只因為模板函數出現在頭文件,就將它們聲明為inline,模板函數和inline并不是必須結對出現的。
條款31:將文件間的編譯依存關系降至最低
為了增加編譯速度,應該減少類文件之間的相互依存性(include),但是類內又常常使用到其他類,不得不相互依存,解決方案是:將類的聲明和定義分開(不同的頭文件),聲明相互依存,而定義不相依存,這樣當定義需要變更時,編譯時不需要再因為依賴而全部編譯。
基于此構想的兩個手段是Handle classes和Interface classes。Handle classes是一個聲明類,一個imp實現類,聲明類中不涉及具體的定義,只有接口聲明,在定義類中include聲明類,而不是繼承。而Interface classes是在接口類中提供純虛函數,作為一個抽象基類,定義類作為其子類來實現具體的定義。
繼承與面向對象設計
條款32:確定你的public繼承是is-a關系
public繼承意味著 is-a 關系,也就是要求,適用于基類身上的每一件事情,是每一件,也一定適用于衍生類身上。有時候,直覺上滿足這一條件的繼承關系,可能并不一定,比如,企鵝是鳥,但并不會飛。
條款33:避免遮掩繼承而來的名稱
就如函數作用域內的變量會掩蓋函數作用域外的同名變量一樣。
衍生類中如果聲明了與基類中同名的函數(無論是虛、非虛,還是其他形式),都會掩蓋掉基類中的所有同名函數,注意,是所有,包括參數不同的重載函數,都會不再可見。此時再通過子類使用其基類中的重載函數(子類沒有聲明接收該參數的重載函數時),都會報錯。
解決方案一是使用using聲明式來在子類中聲明父類的同名函數(重載函數不需要聲明多個),此時父類的各重載函數就是子類可見的了。二是使用轉交函數,即在子類函數的聲明時進行定義,調用父類的某個具體的重載函數(此時由于在聲明時定義,成為inline函數),此舉可以只讓需要的部分父類重載函數于子類可見。
條款34:區分接口繼承和實現繼承
聲明一個純虛函數的目的是為了讓衍生類只繼承其函數接口,而自己進行函數定義實現。
聲明一個非純虛函數的目的是為了讓衍生類繼承該函數的接口和缺省實現(一般實現),如果有特別的操作需求,可以在衍生類中進行實現來覆蓋。如果擔心因此忘記做特異化實現,可以利用純虛函數,在父類給純虛函數一個實現,然后在子類的該函數的實現中調用它,這樣就會記得在需要特異化的子類中進行其他特異化實現。
聲明一個非虛函數的目的是為了讓衍生類完全繼承該函數的接口和實現,也就是聲明該函數的實現方式不得更改,所有子類都表現一致。
條款35:考慮虛函數以外的其他選擇
虛函數(本質是希望子類的實現不同)的替代方案:
- 用public的非虛函數來調用private的虛函數具體實現,非虛函數必須為子類繼承且不得更改,所以它決定了何時調用以及調用前后的處理;虛函數實現可以在子類中覆寫,從而實現多態。
- 將虛函數替換為函數指針成員變量,這樣可以對同一種子類對象賦予不同的函數實現,或者在運行時更改某對象對應的函數實現(添加一個set函數)。
- 用tr1::function成員變量替換虛函數,從而允許包括函數指針在內的任何可調用物搭配一個兼容于需求的簽名式。
- 將虛函數也做成另一個繼承體系類,然后在調用其的類中添加一個指針來指向其對象。
本條款的啟示為:為避免陷入面向對象設計路上因常規而形成的凹洞中,偶爾我們需要對著車輪猛推一把。這個世界還有其他許多道路,值得我們花時間加以研究。
條款36:絕不重新定義繼承而來的非虛函數
不要重新定義繼承而來的非虛函數,理論上,非虛函數的意義就在于父類和子類在該函數上保持一致的實現。
條款37:絕不重新定義繼承而來的缺省參數值
不要重新定義一個繼承而來的函數(虛函數)的缺省參數的值(參數默認值),因為函數是動態綁定(調用指針指向的對象的函數實現),但參數默認值卻是靜態綁定(指針聲明時的類型所設定的默認參數,比如基類設定的)。這會導致兩者不對應,比如:
Base *p = new SubClass();
條款38:通過復合表示 has-a 或者“根據某物實現出”的關系
注意 has-a 和 is-a 的區分。如果是 is-a 的關系,可以用繼承,但如果是 has-a 的關系,應該將一個類作為另一個類的成員變量來使用,以利用該類的能力,而不是去以繼承它的方式使用。
條款39:明智而審慎地使用private繼承
Private繼承意味著“根據某物實現出”,而不是 is-a 的關系。與上面的復合(has-a)很像,但比復合的級別低。當衍生類需要訪問 protected 基類的成員,或需要重新定義繼承而來的虛函數時,可以這么設計。
此外,private繼承可以讓空基類的空間最優化。
條款40:明智而審慎地使用多重繼承
多重繼承確實有正當使用場景,比如public繼承某個接口類的接口(其接口依然是public的),private繼承某個類的實現來協助實現(繼承來的實現為private,只供自己用)。
虛繼承會增加大小、速度、初始化(及賦值)復雜度等成本,如果虛基類不帶任何數據,將是最具使用價值的情況。
模板與泛型編程
條款41:了解隱式接口和編譯期多態
類和模板都支持接口和多態。
類的接口是顯式定義的——函數簽名。多態是通過虛函數在運行期體現的。
模板的接口是隱式的(由模板函數的實現代碼所決定其模板對象需要支持哪些接口),多態通過模板具現化和函數重載解析在編譯期體現,也就是編譯期就可以賦予不同的對象于模板函數。
條款42:了解typename的雙重意義
聲明模板的參數時,前綴關鍵字 class 和 typename 可互換,功能相同。
對于嵌套從屬類型名稱(即依賴于模板參數類型的一個子類型,例如迭代器),必須用typename來修飾,但不能在模板類的基類列和初始化列表中修飾基類。
條款43:學習處理模板化基類內的名稱
如果基類是模板類,那么衍生類直接調用基類的成員函數無法通過編譯器,因為可能會有特化版的模板類針對某個類不聲明該接口函數。
解決方法有:
- 在調用動作前加上“this->”
- 使用using聲明式來在子類中聲明基類的該接口
- 明確指出被調用的函數位于基類:Base<template>::xxx();
以上做法都是承諾被調用的函數一定會在各種特化基類中均聲明。如果沒有聲明,還是會在運行期報錯。
條款44:將與參數無關的代碼抽離templates
任何模板代碼都不該與某個造成膨脹的參數產生相依關系:
- 因非類型模板參數造成的代碼膨脹(比如用尺寸做模板參數導致為不同尺寸的同一對象生成多份相同代碼),往往可消除,做法是將該參數改為函數參數或者類成員變量,而不要放到模板的參數中。
- 因類型參數造成的代碼膨脹(比如int和long有相同的二進制表述,但作為模板參數會產生兩份代碼),往往可降低,做法是讓帶有完全相同二進制表述的具體類型共享實現碼——使用唯一一份底層實現。
條款45:運用成員函數模板接受所有兼容類型
真實指針允許父類指針指向子類對象,如果想要讓自制的智能指針也支持這種對象轉換,那就需要特殊操作,因為一般的模板類(智能指針能指向多種對象,必然是模板類)只能以自身模板聲明的類型來構造。
做法是聲明一個泛化構造函數,也就是定義一個模板構造函數,接收模板參數,聲明一個指向的真實對象指針,聲明一個獲取該對象指針的get函數,用該get函數放在初始化列表中來構造模板類。這樣就能使用一種類型特化出的自制智能指針來構造另一種類型特化出的自制智能指針了。同時,在初始化列表中編譯器會為你檢查是否允許該類型轉換(比如只允許子類往父類的轉換,不能相反)。
雖然這種模板構造函數也能作為復制構造函數使用(用相同類型來構造即可),但編譯器還是會當做你沒有聲明復制構造函數,從而為你創建一個,因此如果想要徹底控制行為,你還是需要自行聲明你的復制構造函數和賦值構造函數。
條款46:需要類型轉換時請為模板定義非成員函數
模板類中的模板函數不支持隱式類型轉換,如果你在調用時傳了一個其他類型的變量,編譯器無法幫你做類型轉換,從而報錯。
解決方案是將該模板函數定義為模板類內的友元模板函數,從而支持了參數的隱式轉換。如果函數的功能比較簡單,可以直接inline,如果比較復雜,可以調用一個類外的定義好的模板函數(此時,友元函數已經給參數做了類型轉換,因此可以調用模板函數了)。
條款47:請使用traits classes表現類型信息
對于模板函數,可能對于接收參數的不同類型,有不同的實現。此時,可以提供一個traits class,其中包含了某一系列類型的類型信息(通常以枚舉區分具體類型),然后,在該類中實現接收多種traits參數的重載工具函數,用來根據標識的不同類進行不同的具體函數操作。這使得該行為能在編譯期就被區分。
條款48:認識模板元編程(TMP)
TMP可將工作由運行期移往編譯器,因而得以實現早期錯誤偵測和更高的執行效率。
實現方式以模板為基礎,因為模板會在編譯時確定,上一條款的traits classes就是一種TMP,依靠模板函數參數不同的重載來在編譯器模擬if else(其在運行期才會判斷)。
另一個例子是用模板來在編譯器實現階乘:
template<unsigned n>
struct Factorial {
enum { value = n * Factorial<n-1>::value };
};
template<>
struct Factorial<0> {
enum { value = 1 };
}
用模板來實現遞歸從而在編譯器實現階乘運算,用參數為0的特異化來做遞歸的終結。
定制 new 和 delete
條款49:了解 new-handler 的行為
在對象new操作分配內存時,如果分配失敗,默認會返回null(老編譯器)或拋出bad_alloc 異常(新編譯器)。
如果想要自定義分配失敗的操作,可以調用 set_new_handler 函數來設置new_handler。
如果想要讓類在構造時自動調用自定義的new_handler,并在構造結束后回到系統默認的new_handler ??梢岳^承一個聲明了set_new_handler函數接口和包含設置與回歸new_handler的new函數的模板類,然后讓你的自定義類繼承自你的類名所特化的該模板類,從而能夠為每一個你的類做一個特化的new_handler函數。
條款50:了解new和delete的合理替換時機
有很多理由讓你想要寫個自定的new和delete,比如改善定制版的效能、對heap運用錯誤進行調試、收集heap使用信息等。也有許多商業或開源的內存分配器供你使用。
條款51:編寫new和delete時需固守常規
自定義的new應該內含一個無窮循環,在其中嘗試分配內存,如果失敗,就該調用new-handler以退出循環。同時它應該有能力處理0 bytes的申請(可以簡單判斷并改為1bytes)。Class專屬版本還要處理衍生類的申請,不要直接調用基類的(大小不同),可以判斷并轉調普通的new函數。
自定義的delete應該可在收到null指針時不做任何事,Class專屬版本還應該處理衍生類的申請,不要直接調用基類的(大小不同),可以判斷并轉調普通的delete函數。
條款52:寫了 placement new 也要寫 placement delete
如果你的new接收的參數除了必定有的size_t外還有其他,就是個placement new。delete類似。
當創建對象時,會先進行new,然后調用構造函數,如果構造出現異常,就需要delete,否則內存泄漏。如果用了placement new,那么編譯器會尋找含有同樣參數的placement delete,否則不會delete,因此必須成對寫接收同樣參數的placement new和placement delete。
同時,為了讓用戶主動使用delete時能進行正確操作,你需要同時定義一個普通形式的delete,來執行和placement delete同樣的特殊實現。
你在類中聲明placement new后,會掩蓋C++提供的new函數,因此除非你確實不想用戶使用默認的new,否則你需要確保它們還可用(條款33)。
雜項討論
條款53:不要輕忽編譯器的警告
對于編譯器編譯時給出的警告信息,最好立即修復,避免后續調試半天來尋找編譯器早就告知你的問題。
條款54:讓自己熟悉包括TR1在內的標準程序庫
C++98的標準程序庫有:
- STL
- Iostreams,包括cin、cout、cerr、clog等
- 國際化支持
- 數值處理
- 異常階層體系
- C89標準程序庫
而TR1是新的一系列組件,在std內的tr1命名空間中,比如:std::tr1::shared_ptr。它包含:
- 智能指針,包括shared_ptr和weak_ptr。
- function:支持以函數簽名(出參類型+入參類型)作為模板
- bind:綁定器
- 無序hash表,用以實現無序的set、multiset、map、multimap
- 正則表達式
- tuples:擴充pair,能持有任意個數的對象,類似python中的tuples。
- array:STL化的數組,支持begin和end,不過其大小固定,不適用動態內存。
- mem_fn
- reference_wrapper:讓引用的行為更像對象,可以被容器持有。
- 隨機數生成工具:大大超越rand
- 數學特殊函數:多種數學函數
- C99兼容擴充。
- type traits,使用見條款47,提供類型的編譯期信息。
- result_of:是個模板,用來推到函數調用的返回類型。
條款55:讓自己熟悉Boost
Boost是一個程序庫,其由C++標準委員會成員創設,可視為一個“可被加入標準C++的各種功能”的測試場,涵蓋眾多經過多輪復核的優質程序,如果想知道當前C++最高技術水平、想一瞥未來C++的可能長相?看看Boost吧。
http://boost.org