這是作者為《C++ Primer(第4版)(評注版)》寫的序言,文中“本書”指的是這本書評注版。
B.1 為什么要學習C++
2009年本書作者Stanley Lippman先生應邀來華參加上海祝成科技舉辦的C++技術大會,他表示人們現在還用C++的唯一理由是其性能。相比之下,Java、C#、Python等語言更加易學易用并且開發工具豐富,它們的開發效率都高于C++。但C++目前仍然是運行最快的語言(見編程語言性能對比網站和Google員工寫的語言性能對比論文(http://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf)),如果你的應用領域確實在乎這個性能,那么C++是不二之選。
這里略舉幾個例子(C++之父Bjarne Stroustrup維護的C++用戶列表)。對于手持設備而言,提高運行效率意味著完成相同的任務需要更少的電能,從而延長設備的操作時間,增強用戶體驗。對于嵌入式(初窺C++在嵌入式系統中的應用,參見http://aristeia.com/TalkNotes/MISRA_Day_2010.pdf)設備而言,提高運行效率意味著:實現相同的功能可以選用較低檔的處理器和較少的存儲器,降低單個設備的成本;如果設備銷量達到一定規模,可以彌補C++開發的成本。對于分布式系統而言,提高10%性能就意味著節約10%的機器和能源。如果系統大到一定的規模(數千臺服務器),值得用程序員的時間去換取機器的時間和數量,可以降低總體成本。另外,對于某些延遲敏感的應用(游戲(Milo Yip在《C++強大背后》提到大部分游戲引擎(如Unreal/Source(起源引擎))及中間件(如Havok(用于游戲和模擬中的物理模擬)/FMOD(用于處理音頻資源、音效設計和音頻播放等功能))是C++實現的(http://www.cnblogs.com/miloyip/archive/2010/09/17/behind_cplusplus.html)),金融交易),通常不能容忍垃圾收集(GC)帶來的不確定延時,而C++可以自動并精確地控制對象銷毀和內存釋放時機(參見孟巖的《垃圾收集機制批判》:“C++利用智能指針達成的效果是,一旦某對象不再被引用,系統刻不容緩,立刻回收內存。這通常發生在關鍵任務完成后的清理(clean up)時期,不會影響關鍵任務的實時性,同時,內存里所有對象都是有用的,絕對沒有垃圾空占內存。”,http://blog.csdb.net/myan/article/details/1906)。作者曾經不止一次見到,出于性能(特別是及時性方面的)原因,用C++重寫現有的Java或C#程序。
C++之父Bjarne Stroustrup把C++定位于偏重系統編程(system programming)(有人半開玩笑地說:“所謂系統編程,就是那些CPU時間比程序員的時間更重要的工作。”)的通用程序設計語言,開發信息基礎架構(infrastructure)是C++的重要用途之一(《Software Development for Infrastructure》)。Herb Sutter總結道(Herb Sutter在C++ and Beyond 2011會議上的開場演講:《Why C++?》),C++注重運行效率(efficiency)、靈活性(flexibility)(這里的靈活性指的是編譯器不阻止你干你想干的事情,比如為了追求運行效率而實現即時編譯(just-in-time compilation,JIT編譯,一種在程序運行時將代碼轉換為機器代碼的技術。與傳統的靜態編譯不同,JIT編譯是在程序運行時動態地進行代碼轉換))和抽象能力(abstraction),并為此付出了生產力(productivity)方面的代價(作者曾向Stanley Lippman介紹目前作者在Linux下的工作環境(編輯器、編譯器、調試器),他表示這跟他在1970年代的工作環境相差無幾,可見C++在開發工具方面的落后。另外C++的編譯運行調試周期也比現代的語言長,這多少影響了工作效率)。用本書作者的話來說,就是“C++ is about efficient programming with abstractions”(C++的核心價值在于能寫出“運行效率不打折扣的抽象”)。
要想發揮C++的性能優勢,程序員需要對語言本身及各種操作的代價有深入的了解(《Technical Report on C++Performance》,http://www.open-std.org/jtc1/sc22/wg21/docs/18015.html),特別要避免不必要的對象創建(可參考Scott Meyers的《Effective C++ in an Embedded Environment》講義,http://www.artima.com/shop/effective_cpp_in_an_embedded_environment)。例如下面這個函數如果漏寫了&,功能還是正確的,但性能將會大打折扣。編譯器和單元測試都無法幫我們查出此類錯誤,程序員自己在編碼時須得小心在意。
inline int find_longest(const std::vector<std::string> &words)
{// std::max_element(words.begin(), words.end(), LengthCompare());
}
在現代 CPU體系結構下,C++的性能優勢很大程度上得益于對內存布局(memory layout)的精確控制,從而優化內存訪問的局部性(locality of reference)并充分利用內存階層(memory hierarchy,內存階層通常由寄存器、高速緩存、主存和輔助存儲器組成)提速(我們知道std::list的任一位置插入是O(1)操作,而std::vector的任一位置插入是O(N)操作,但由于vector的元素布局更加緊湊(compact),很多時候vector的隨機插入性能甚至會高于list。這也佐證vector是首選容器)。可參考Scott Meyers的講義《CPU Caches and Why You Care》(http://aristeia.com/TalkNotes/ACCU2011_CPUCaches.pdf)、Herb Sutter的講義《Machine Architecture》和任何一本現代的計算機體系結構教材(《計算機體系結構:量化研究方法》、《計算機組成與設計:硬件/軟件接口》、《深入理解計算機系統》等)。這一點優勢在近期內不會被基于GC的語言趕上(Bjarne Stroustrup有一篇論文《Abstraction and the C++ machine model》對比了C++和Java的對象內存布局)。
C++的協作性不如C、Java、Python,開源項目也比這幾個語言少得多,因此在TIOBE語言流行榜(一種用于衡量編程語言流行程度的指標)中節節下滑。但是據作者所知,很多企業內部使用C++來構建自己的分布式系統基礎架構,并且有替換Java開源實現的趨勢。
B.2 學習C++只需要讀一本大部頭
C++不是特性(features)最豐富的語言,卻是最復雜的語言,諸多語言特性相互干擾,使其復雜度成倍增加。鑒于其學習難度和知識點之間的關聯性,恐怕不能用“粗粗看看語法,就擼起袖子開干,邊查Google邊學習”(語出孟巖《快速掌握一個語言最常用的50%》,http://blog.csdn.net/myan/article/details/3144661)這種方式來學習C++,那樣很容易掉到陷阱里或養成壞的編程習慣。如果想成為專業C++開發者,全面而深入地了解這門復雜語言及其標準庫,你需要一本系統而權威(“權威”的意思是說你不用擔心作者講錯了,能達到這個水準的C++圖書作者全世界也屈指可數)的書,這樣的書必定會是一本八九百頁的大部頭(同樣篇幅的Java、C#、Python教材可以從語言、標準庫一路講到多線程、網絡編程、圖形編程)。
兼具系統性和權威性的C++教材有兩本,C++之父Bjarne Stroustrup的代表作《The C++ Programming Language》和Stanley Lippman的這本《C++ Primer》。侯捷先生評價道:“泰山北斗已現,又何必案牘勞形于墨瀚書海之中!這兩本書都從C++盤古開天以來,一路改版,斬將擎旗,追奔逐北,成就一生榮光。”(侯捷《大道之行也——C++ Primer 3/e譯序》)
從實用的角度,這兩本書讀一本即可,因為它們覆蓋的C++知識點相差無幾。就作者個人的閱讀體驗而言,Primer更易讀一些,作者10年前深入學習C++正是用的《C++ Primer(第3版)》。這次借評注的機會仔細閱讀了《C++ Primer(第4版)》,感覺像在讀一本完全不同的新書。第4版內容組織及文字表達比第3版進步很多(Bjarne Stroustrup在《Programming——Principles and Practice Using C++》的參考文獻中引用了本書,并特別注明“use only the 4th edition”),第3版可謂“事無巨細、面面俱到”,第4版則重點突出、詳略得當,甚至篇幅也縮短了,這多半歸功于新加盟的作者Barbara Moo。
《C++ Primer(第4版)》講什么?適合誰讀?
這是一本C++語言的教程,不是編程教程。本書不講八皇后問題、Huffman編碼、漢諾塔、約瑟夫環、大整數運算等經典編程例題,本書的例子和習題往往都跟C++本身直接相關。本書的主要內容是精解C++語法(syntax)與語意(semantics),并介紹C++標準庫的大部分內容(含STL)。“這本書在全世界C++教學領域的突出和重要,已經無須我再贅言。”(侯捷《C++ Primer 4/e譯序》)
本書適合C++語言的初學者,但不適合編程初學者。換言之,這本書可以是你的第一本C++書,但恐怕不能作為第一本編程書。如果你不知道什么是變量、賦值、分支、條件、循環、函數,你需要一本更加初級的書(如果沒有時間精讀《Programming——Principles and Practice Using C++》這本大部頭,短小精干的《Accelerated C++》亦是上佳之選。另外如果想從C語言入手,作者推薦裘宗燕老師的《從問題到程序:程序設計與C語言引論》(用最新版)),本書第1章可用做自測題。
如果你已經學過一門編程語言,并且打算成為專業C++開發者,從《C++ Primer(第4版)》入手不會讓你走彎路。值得特別說明的是,學習本書不需要事先具備C語言知識。相反,這本書叫你編寫真正的C++程序,而不是披著C++外衣的C程序。
《C++ Primer(第4版)》的定位是語言教材,不是語言規格書,它并沒有面面俱到地談到C++的每一個角落,而是重點講解C++程序員日常工作中真正有用的、必須掌握的語言設施和標準庫(本書把iostream的格式化輸出放到附錄,徹底不談locale/facet,可謂匠心獨運)。本書的作者一點也不炫耀自己的知識和技巧,雖然他們有十足的資本(Stanley Lippman曾說:“Virtual base class support wanders off into the Byzantine… The material is simply too esoteric to warrant discussion…”(虛擬基類的支持變得非常復雜難懂,好像進入了拜占庭迷宮一樣。而且這個話題非常深奧,不值得討論))。這本書用詞非常嚴謹(沒有那些似是而非的比喻),用詞平和,講解細致,讀起來并不枯燥。特別是如果你已經有一定的編程經驗,在閱讀時不妨思考如何用C++來更好地完成以往的編程任務。
盡管本書篇幅近900頁,但其內容還是十分緊湊的,很多地方讀一個句子就值得寫一小段代碼去驗證。為節省篇幅,本書經常修改前文代碼中的一兩行,來說明新的知識點,值得把每一行代碼敲到機器中去驗證。習題當然也不能輕易放過。
《C++ Primer(第4版)》體現了現代C++教學與編程理念:在現成的高質量類庫上構建自己的程序,而不是什么都從頭自己寫。這本書在第3章介紹了string和vector這兩個常用的class,立刻就能寫出很多有用的程序。但作者不是一次性把string的上百個成員函數一一列舉,而是有選擇地先講解了最常用的那幾個函數,充分體現了本書作為教材而不是手冊的定位。
《C++ Primer(第4版)》的代碼示例質量很高,不是那種隨手寫的玩具代碼。第10.4.2節實現了帶禁用詞的單詞計數,第10.6利用標準庫容器簡潔地實現了基于倒排索引思路的文本檢索,第16.9節又用面向對象方法擴充了文本檢索的功能,支持布爾查詢。值得一提的是,這本書講解繼承和多態時舉的例子符合Liskov替換原則(根據這個原則,任何基類的對象都應該能夠被其子類對象替換,而不影響程序的正確性),是正宗的面向對象。相反,某些教材以復用基類代碼為目的,常以“人、學生、老師、教授”或“雇員、經理、銷售、合同工”為例,這是誤用了面向對象的“復用”。
《C++ Primer(第4版)》出版于2005年,遵循2003年的C++語言標準(基本等同于1998年的出版C++標準,修正了編譯器作者關心的一些問題,與普通程序員無關)。C++新標準已于2011年定案(稱為C++11),本書不涉及TR1(TR1是2005年的C++標準庫的一次擴充,增加了智能指針、bind/function、哈希表、正則表達式等)和C++11,這并不意味著這本書過時了(第4版的作者正在編寫《C++ Primer(第5版)》,會包含C++11的內容)。相反,這本書里沉淀的都是當前廣泛使用的C++編程實踐,學習它可謂正當時。評注版也不會越俎代庖地介紹這些新內容,但是會指出哪些語言設施已在新標準中廢棄,避免讀者浪費精力。
《C++ Primer(第4版)》是平臺中立的,并不針對特定的編譯器或操作系統。目前最主流的C++編譯器有兩個,GNU G++和微軟Visual C++。實際上,這兩個編譯器陣營基本上“模塑”(G++統治了Linux,并且能用在很多Unix系統上;Visual C++統治了Windows。其他C++編譯器的行為通常要向它們靠攏,例如Intel C++在Linux上要兼容G++,而在Windows上要兼容Visual C++)了C++語言的行為。理論上講,C++語言的行為是由C++標準規定的。但是C++不像其他很多語言有“官方參考實現”(曾經是Cfront,本書作者正是其主要開發者,http://www.softwarepreservation.org/projects/c_plus_plus),因此C++的行為實際上是由語言標準、幾大主流編譯器、現有不計其數的C++產品代碼共同確定的,三者相互制約。C++編譯器不光要盡可能符合標準,同時也要遵循目標平臺的成文或不成文規范和約定,例如高效地利用硬件資源、兼容操作系統提供的C語言接口等等。在C++標準沒有明文規定的地方,C++編譯器也不能隨心所欲地自由發揮。學習C++的要點之一是明白哪些行為是由標準保證,哪些是由實現(軟硬件平臺和編譯器)保證的(包括C++標準有規定,但編譯器拒絕遵循的,http://stackoverflow.com/questions/3931312),哪些是編譯器自由實現,沒有保證的;換言之,明白哪些程序行為是可依賴的。從學習的角度,作者建議如果有條件不妨兩個編譯器都用,相互比照,避免把編譯器和平臺特定的行為誤解為C++語言規定的行為(G++是免費的,可使用較新的4.x版,最好32-bit和64-bit一起用,因為服務端已經普及64-bit編程。微軟也有免費的C++編譯器,可考慮用Visual C++ 2010 Express(Express指它是針對學術和個人使用的免費版本),建議不要用老掉牙的Visual C++ 6.0作為學習平臺)。盡管不是每個人都需要寫跨平臺的代碼,但也大可不必自我限定在編譯器的某個特定版本,畢竟編譯器是會升級的。
本著“練從難處練,用從易處用”的精神,建議在命令行下編譯運行本書的示例代碼,并盡量少用調試器。另外,值得了解C++的編譯鏈接模型(可參考陳碩寫的《C++工程實踐經驗談》中的“C++編譯模型精要”一節(本書第十章)),這樣才能不被實際開發中遇到的編譯錯誤或鏈接錯誤絆住手腳(C++不像現代語言那樣有完善的模塊(module)和包(packet)設施,它從C語言繼承了頭文件、源文件、庫文件等古老的模塊化機制,這套機制相對較為脆弱,需要花一定時間學習規范的做法,避免誤用)。
就學習C++語言本身,有幾個練習非常值得一做。這不是“重復發明輪子”,而是必要的編程練習,幫助你熟悉、掌握這門語言。一是寫一個復數類或者大整數類(大整數類可以以std::vector為成員變量,避免手動資源管理),實現基本的加減乘運算,熟悉封裝與數據抽象。二是寫一個字符串類,熟悉內存管理與拷貝控制。三是寫一個簡化的vector<T>
類模板,熟悉基本的模板編程,你的這個vector應該能放入int和std::string等元素類型。四是寫一個表達式計算器,實現一個節點類的繼承體系(圖B-1 右),體會面向對象編程。前三個練習是寫獨立的值語義的類,第四個練習是對象語義,同時要考慮類與類之間的關系。
表示式計算器能把四則運算式3+2×4解析為圖B-1左圖的表達式樹(“解析”可以用數據結構課程介紹的逆波蘭表達式方法,也可以用編譯原理中介紹的遞歸下降法,還可以用專門的Packrat算法),對根節點調用calculate()虛函數就能算出表達式的值。做完之后還可以再擴充功能,比如支持三角函數和變量。
在寫完面向對象版的表達式樹之后,還可以略微嘗試泛型編程。比如把類的繼承體系簡化為圖B-2,然后用BinaryNode<std::plus<double>>
和BinaryNode<std::multipies<double>>
來具現化BinaryNode<T>
類模板(std::plus和std::multipies類對象可接受兩個參數來實現加和乘),通過控制模板參數的類型來實現不同的運算。
在表達式樹這個例子中,節點對象時動態創建的,值得思考:如何才能安全地、不重不漏地釋放內存。本書第15.8節的Handle可供參考(C++的面向對象基礎設施相對于現代的語言而言顯得很簡陋,現在C++也不再以“支持面向對象”為賣點了)。
C++難學嗎?“能夠靠讀書、看文章、讀代碼、做練習學會的東西沒什么門檻,智力正常的人只要愿意花工夫,到不難達到(不錯)的程度。”(孟巖《技術路線的選擇重要但不具有決定性》,http://blog.csdn.net/myan/article/details/3247071)C++好書很多,不過優秀的C++開源代碼很少,而且風格迥異(從代碼風格上往往能判斷項目成型的時代)。這里按個人口味和經驗列幾個供讀者參考閱讀:Google的Protobuf、leveldb(一個開源的、快速、輕量級的鍵值存儲引擎,由Google開發)、PCRE(Perl Compatible Regular Expressions,一個支持Perl正則表達式語法的正則表達式庫)的C++封裝,作者自己寫的muduo網絡庫。這些代碼都不長,功能明確,閱讀難度不大。如果有時間,還可以讀一讀Chromium中的基礎庫源碼。在讀Google開源的C++代碼時要連注釋一起細讀。不建議一開始就讀STL或Boost的源碼,因為編寫通用C++模板庫和編寫C++應用程序的知識體系相差很大。另外可以考慮讀一些優秀的C或Java開源項目,并思考是否可以用C++更好地實現或封裝之(特別是資源管理方面能否避免手動清理)。
B.3 繼續前進
作者能夠隨手列出十幾本C++好書,但是從實用角度出發,這里只舉兩三本必讀的書。讀過《C++ Primer》和這幾本好書之后,想必讀者已能自行識別C++圖書的優劣,可以根據項目需要加以鉆研。
第一本是《Effective C++中文版(第3版)》(Scott Meyers著,侯捷譯,電子工業出版社出版)[EC3]。學習語法是一回事,高效地運用這門語言是另一回事。C++是一個遍布陷阱的語言,吸取專家經驗尤為重要,既能快速提高眼界,又能避免重蹈覆轍。加上《C++ Primer》這本書包含的C++知識足以應付日常應用程序開發。
作者假定讀者一定會閱讀這本書,因此在評注中不引用(《Effective C++中文版(第3版)》)的任何章節。
《Effective C++中文版(第3版)》的內容也反映了C++用法的進步。第2版建議“總是讓基類擁有虛析構函數”,第3版改為“為多態基類聲明虛析構函數”。因為在C++中,“繼承”不光只有面向對象這一種用途,即C++的繼承不一定是為了覆寫(override)基類的虛函數。第2版花了很多筆墨介紹淺拷貝和深拷貝,以及對指針成員變量的處理(Andrew Koenig的《Teaching C++ Badly: Introduce Constructors and Destructors at the Same Time》,http://drdobbs.com/blogs/cpp/229500116)。第3版則提議,對于多數class而言,要么直接禁用拷貝構造函數和賦值操作符,要么通過選用合適的成員變量類型(能自動管理資源的std::string、std::vector、boost::shared_ptr等等,這樣多數class連析構函數都不必寫),使得編譯器默認生成的這兩個成員函數就能正常工作。
什么是C++編程中最重要的編程技法(idiom)?作者認為是“用對象來管理資源”,即RAII。資源包括動態分配的內存(“分配內存”包括在堆(heap)上創建對象),也包括打開的文件、TCP網絡連接、數據庫連接、互斥鎖等等。借助RAII,我們可以把資源管理和對象生命期管理等同起來,而對象生命期管理在現代C++里根本不困難(用智能指針),只需要花幾天時間熟悉幾個智能指針(包括TR1中的shared_ptr、weak_ptr,還有更簡單的boost::scoped_ptr)的基本用法即可。學會了這三招兩式,現代的C++程序中可以完全不寫delete,也不必為指針和內存錯誤操心。現在C++程序里出現資源和內存泄漏的唯一可能是循環引用,一旦發現,也很容易修正設計和代碼。這方面的詳細內容請參考《Effective C++中文版(第3版)》的第3章“資源管理”。
C++是目前唯一能實現自動化資源管理的語言,C語言完全靠手工釋放資源,而其他基于垃圾收集的語言只能能自動清理內存,而不能自動清理其他資源(Java 7有try-with-resources語句,Python有with語句,C#有using語句,可以自動清理棧上的資源,但對生命期大于局部作用域的資源無能為力,需要程序員手工管理)(網絡連接,數據庫連接等)。
除了智能指針,TR1中的bind/function也十分值得投入精力去學一學(孟巖的《function/bind的救贖(上)》,http://blog.csdn.net/myan/article/details/5928531)。讓你從一個嶄新的視角,重新審視類與類之間的關系。Stephan T. Lavavej有一套PPT介紹TR1的這幾個主要部件(http://blogs.msdn.com/b/vcblog/archive/2008/02/22/tr1-slide-decks.aspx)。
第二本書,如果讀者還是在校學生,已經學過數據結構課程(最好再學一點基礎的離散數學)的話,可以考慮讀一讀《泛型編程與STL》(Matthew Austern著,侯捷譯,中國電力出版社);如果已經工作,學完《C++ Primer》立刻就要參加C++項目開發,那么推薦閱讀《C++編程規范》(Herb Sutter等著,劉基誠譯,人民郵電出版社出版,這本書的繁體版由侯捷先生和作者翻譯)[CSS]。
泛型編程有一套自己的術語,如concept(指對模板參數的一組要求或限制,它描述了模板參數所需要具備的特定特性,例如某個類型必須具有某種成員函數、某種操作符重載等等)、model(指滿足某個concept的具體類型或模板)、refinement(指在一個已定義的concept的基礎上,對其進行擴展和補充,以適應更特定的需求和要求,通過refinement,我們可以在已有的concept的基礎上添加新的要求、約束和行為,從而創建一個更具體、更精確的concept)等等,理解這套術語才能閱讀泛型程序庫的文檔。即便不掌握泛型編程作為一種程序設計方法,也要掌握C++中以泛型思維設計出來的標準容器庫和算法庫(STL)。坊間面向對象的書琳瑯滿目,學習機會也很多,而泛型編程只有這么一本,讀之可以開闊視野,并且加深對STL的理解(特別是迭代器(侯捷先生的《芝麻開門:從Iterator談起》))和應用。
C++模板是一種強大的抽象手段,作者不贊同每個人都把精力花在鉆研艱深的模板語法和技巧上。從實用角度,能在應用程序中寫寫簡單的函數模板和類模板即可(以type traits(類型特性,如是否是常量類型(const)、是否是指針類型、是否是引用類型等,C++標準庫提供了一組類型特性模板類和函數,如std::is_const、std::is_pointer、std::is_reference)為限),并非每個人都要去寫公用的模板庫。
由于C++語言過于龐大復雜,作者見過的開發團隊都對其剪裁使用(孟巖的《編程語言的層次觀點——兼談C++的剪裁方案》,http://blog.csdn.net/myan/article/details/1920)。往往團隊越大,項目成立時間越早,剪裁得越厲害,也越接近C。制定一份好的編程規范相當不容易。若規范定得太緊(比如定位團隊成員知識能力的交集),程序員束手束腳,限制了生產力,對程序員個人發展也不利(一個人通常不會在一個團隊工作一輩子,其他團隊可能有不同的C++剪裁使用方式,程序員要有“一桶水”的本事,才能應付不同形狀大小的水碗)。若規范定得太松(定位團隊成員知識能力的并集),項目內代碼風格迥異,學習交流協作成本上升,恐怕對生產力也不利。由兩位頂級專家合寫的《C++編程規范》一書可謂是現代C++編程規范的范本。
《C++編程規范》同時也是專家經驗一類的書,這本書篇幅比《Effective C++中文版(第3版))》短小,條款數目卻多了近一倍,可謂言簡意賅。有的條款看了就明白,照做即可:
1.第1條,以告警高級別編譯代碼,確保編譯器無警告。
2.第31條,避免寫出依賴于函數實參求值順序的代碼。C++操作符的優先級、結合性與表達式的求值順序是無關的。裘宗燕老師寫的《C/C++語言中表達式的求值》(http://www.math.pku.edu.cn/teachers/qiuzy/technotes/expression2009.pdf)一文對此有明確的說明。
3.第35條,避免繼承“并非設計作為基類使用”的class。
4.第43條,明智地使用pimpl。這是編寫C++動態鏈接庫的必備手法,可以最大限度地提高二進制兼容性。
5.第56條,盡量提供不會失敗的swap()函數。有了swap()函數,我們在自定義賦值操作符時就不必檢查自賦值了。
6.第59條,不要在頭文件中或#include之前寫using。
7.第73條,以by value方式拋出異常(這樣在異常被捕獲后,即使異常拋出的作用域已經結束,捕獲的代碼仍然可以訪問異常對象的副本),以by reference方式捕獲異常(通過引用傳遞不需要額外的拷貝)。
8.第76條,優先考慮vector,其次再選擇適當的容器。
9.第79條,容器內只可存放value和smart pointer。
有的條款則需要相當的設計與編碼經驗才能解其中三味:
1.第5條,為每個物體(entity)分配一個內聚任務。
2.第6條,正確性、簡單性、清晰性居首。
3.第8、9條,不要過早優化;不要過早劣化。
4.第22條,將依賴關系最小化。避免循環依賴。
5.第32條,搞清楚你寫的是哪一種class。明白value class、base class、trait class(常見用途是實現類型特征,例如判斷一個類型是否具有某種成員函數、是否滿足某種概念或屬性等,通過使用Trait class,可以根據類型的特征來編寫通用的代碼,而不需要依賴具體的類型)、policy class(可作為模板實參傳遞給其他模板類,如一個內存管理模板類,對于內存分配方式,我們可以寫多個policy class,如動態分配policy class、靜態分配policy class等,然后根據需要將某個policy class傳遞給內存管理模板類)、exception class各有其作用,寫法也不盡相同。
7.第33條,盡可能寫小型class,避免寫出“大怪獸(monolithic class)”。
8.第37條,public繼承意味著可替換性。繼承非為復用,乃為被復用。
9.第57條,將class類型及其非成員函數接口放入同一個namespace。
值得一提的是,《C++編程規范》是出發點,但不是一份終極規范。例如Google的C++編程規范和LLVM編程規范(http://llvm.org/docs/CodingStandards.html#ci_rtti_exceptions)都明確禁用異常,這跟這本書的推薦做法正好相反。
B.4 評注版使用說明
評注版采用大16開印刷,在保留原書版式的前提下,對其進行了重新分頁,評注的文字與正文左右分欄并列排版。另外,本書已依據原書2010年第11次印刷的版本進行了全面修訂。為了節省篇幅,原書每章末尾的小結、術語表及書末的索引都沒有印在評注版中,而是做成PDF供讀者下載,這也方便讀者檢索。評注的目的是幫助初次學習C++的讀者快速深入掌握這門語言的核心知識,澄清一些概念、比較與其他語言的不同、補充實踐中的注意事項等。評注的內容約占全書篇幅的15%,大致比例是三分評、七分注,并有一些補白的內容(第10章繪制了數據結構示意圖,第11章補充lower_bound和upper_bound的示例)。如果讀者拿不定主意是否購買,可以先翻一翻第5章。作者在評注中不談C++11(從Scott Meyers的講義可以快速學習C++11,http://www.artima.com/shop/overview_of_the_new_cpp),但會略微涉及TR1,因為TR1已經投入實用。
為了不打斷讀者閱讀的思路,評注中不會給URL鏈接,評注中偶爾會引用《C++編程規范》的條款,以[CSS]標明,這些條款的標題已在前文列出。另外評注中出現的soXXXXXX表示http://stackoverflow.com/questions/XXXXXX網址。