拳拳到肉 擲地鏗鏘
三本 OOP 絕佳小書

侯捷 2000.06.12


拿破崙雖然是個矮個子,一生叱吒卻儼然歷史巨人。今天我要介紹的三本書,雖然輕薄短小有如拿破崙的身材,在 C++/OOP 領域裡,其份量與影響卻也有著拿破崙般的輝煌燦爛。

說它們輕薄短小,是的,讓數字說話:三本書合起來才256+318+208=782 頁,只比 C++ 語言知名教本 C++ Primer 3/e一半篇幅再多一些而已,比起 C++ 語言權威著作 The C++ Programming Language 3/e 也才達到三分之二的頁數份量。逛書店時一個不留神,只怕你便遺漏了這些小書的存在。但如果你真遺漏了它們的存在,實在是你的莫大損失。

就我個人的編程經驗,以及我的教學經驗(對象為業界工程師或大學生),只要是 C++/OOP 設計思維與語言運用本身的問題,非關 problem domain,百分之九十以上皆可在這三本書籍中找到直接或間接的答案。這三本書是:

Effective C++ 2/e : 50 Specific Ways to Improve Your Programs and Designs
More Effective C++ : 35 New Ways to Improve Your Programs and Designs
Exceptional C++ : 47 Engineering Puzzles, Programming Problems, and Solutions

(註:前兩本書亦有 CD 產品(HTML 格式),以兩書合一的方式出售,交叉索引甚為方便)

書名副標清楚告訴我們,這些書籍的特色就是以一個個的條款來說明一些值得奉行或是必須避免的動作。以條款的方式進行,一方面因為它們都不是 C++ 語言教本,而是更高層次的書籍,是給有語言基礎(最好還有實際工作經驗)的人看的書,所以可以集中火力在特定主題上,另一方面實乃因為條款型式可以強化主題,加深印象,提昇查閱檢索的方便性與價值。

書中某些條款在 C++ 語言書籍裡或多或少也都有提到 — 雖然解釋的深度可能不足。我舉個例子,Effective C++ 的條款13說「Initialization list 中的 members 初始化排列次序應該和其在 class 內的宣告次序相同」,C++ Primer 3/e 的 p721~p722 對此有相同意義的描述。再舉個例子,Effective C++ 的條款14說「總是讓 base class 擁有 virtual destructor」,這在 C++ Primer 3/e的 p933 亦有相同意義的描述。諸如此類,不勝枚舉。但你是不是感覺,那些金科玉律被淹沒在語言百科型書籍的細節叢林之中!

每一條準則都簡短、明確、容易記憶,可以確實提昇你的軟體效能。每一條準則都是經驗豐富的 C++ 程式員的心血結晶,告訴你哪些是幾乎總是需要奉行的動作,哪些是幾乎肯定要避免的事情。某些條款甚至到達 patterns 的水準()。

所謂 patterns,在指程式設計過程中,開發人員常需面對並解決的某些問題,被有系統地整理出解法,謂之 patterns(或譯為「樣式」)。Design Patterns 是此域中的經典書籍,系統化地將可應用於眾多領域之 23 個基本 patterns 加以分類整理。書中所提的 patterns 名稱,幾乎成為物件導向設計領域的標準辭彙。

以上三本小書的功用不僅在提綱契領地點出重點,也在於對每個主題有深刻的討論。在這些書籍中,你會發現一些忠告,告訴你應該做些什麼,為什麼如此;也告訴你不應該做些什麼,又為什麼如此。基本而言當然 whys 比 whats 更重要,這便是這些書籍最有價值的地方。至於從速食的角度來看,檢閱一系列準則,也比強記一或二本龐雜的教科書更輕鬆方便得多。



effective-cpp-2e.jpg (25375 bytes)

【基本資料】
書名:Effective C++ 2/e : 50 Specific Ways to Improve Your Programs and Designs
作者:Scott Meyers
出版:Addison Wesley, 0-201-92488-9, 1998
定價:US$ 37.95
頁數:256

Scott Meyers 的這本書籍,成名已久,同時也是此類書籍的濫觴。這本書在 1992 年便有了第一版,我手上的第一版是 1996/12 的第 12 刷。刷次原不能代表什麼,不過我在第二版的封底看到,出版公司宣稱第一版銷售超過十萬本(不含各種譯本)。銷售量能否代表什麼呢?銷售量低或許不一定能代表什麼,銷售量高則相當程度地表示受到讀者的肯定。以此書訴求之技術層面而言,這個銷售量是驚人的。(當然要是比起大陸某 C 語言書籍銷售270 萬本,以及某 Visual Basic 書籍銷售破千萬本,那是小巫見大巫了)

只要是在 C++ 領域裡打滾的程式員,馬上便可以從 Meyers 整理出來的 50 個條款看出此書的價值。如果你拿 C++ 來討生活,稍稍閱讀幾個條款的深度後,不掏腰包者幾稀。有些條款涉及的語意層面較低,用以對容易出錯或混淆的數種程式寫法提出警告。另有一些條款涉及的層次比較高,如第六章中對於各種繼承型式的討論、以及 classes 之間除了繼承以外的其他關係的討論。至於條款E45「知道 C++ 編譯器默默為我們完成和呼叫哪些函式」,以及條款M24「瞭解 virtual functions、multiple inheritance、virtual base classes、RTTI 所需的成本」,下涉物件模型與編譯器層面,相當深入。套用我在「C++ 的沉迷與愛戀」一文中的用辭,此書在高熱和驟冷之間,在高階和低階之間焠鍊、循環、震盪。

本書第一頁列有它所獲得的讚譽。對於讀者,這是一份值得參考的資料,我試著把它們譯為中文,條列於下:

※我必須真心地讚美 Meyers 的書,…這本書在記憶體管理架構等主題上給予讀者卓越的引導,並對不同型式之 C++ 繼承機制有極佳的解釋。
- New York Computerist

※在你開始著手第一個真正的 C++ 專案之前,你應該閱讀本書;在你獲得一些實務經驗之後,你應該再讀一遍。
- comp.lang.c++

※本書有一個子標題「改善你的程式技術與設計思維的 50 個有效作法 」。作者不只提供你撰寫 C++ 碼時應該遵循的明白規則,也提供了深入的解釋與範例。
- Sun Expert

※我慎重推薦 Effective C++ 給任何渴望在中高階層面精通C++ 的人。
- The C User's Journal

※在各種寫給中高階程式員看的 C++ 書籍中,這是我所見過最棒的一本。作者以一系列短文敘述 C++ 程式員遭遇的常見問題。…本書既有趣又有用,在程式設計書籍中誠屬罕見。
- comp.lang.c++

※本書在取材範圍和風味上類似另一本由 William Strunk和 E.B. White 合著的小書 The Elements of Style;至少二者在我的書架上距離不遠。…這是一本十分謹慎而適度的小書,有著清楚的目標,並且完成了它們。
- C++ Report

※這本書內含對 C++ 開發工作的許多實用忠告。
- DEC Professional

※C++ 程式員不只應該擁有這本書,而且應該確實運用這本書。書中的文字極易拿來實際運用。交叉參考與索引的功夫做得很好。
- Computer Language

※這是一本 193 頁的傑作。…我保證 50 個條款中必定有某一些會攫取你的注意力並對你產生啟蒙作用,你的謹慎投資將獲得回報。…這是一本文筆優越、真材實料的書籍,目標瞄準重視流暢與效率的所有 C++ 程式員。
- Stan Kelley-Bootle, UNIX Review

※這本絕妙好書提供了 50 個招數,幫助我們把 C++ 運用得更好。每一位 C++ 程式員桌上都應該有這一本書。…在提昇 C++ 程式設計的整體品質上, Scott Meyers 這份珍貴的禮物或許比業內任何人士的貢獻都大。』
-- Jesse Liberty, C++ Report

下面列出 Effective C++ 2/e 的各個條款(我試著譯為中文),一方面讓這篇書評的實用性更高一些,一方面就讓這些條款標題直接彰顯它們的價值吧。

第一章:改變舊有的 C 習慣
條款1:儘量以 const 和 inline 取代 #define
條款2:儘量以 <iostream> 取代 <stdio.h>
條款3:儘量以 new 和 delete 取代 malloc() 和 free()
條款4:儘量使用 C++ 風格的註解型式

第二章:記憶體管理(Memory Management)
條款5:使用相同型式的 new 和 delete
條款6:記得在 destructor 中以 delete 對付 pointer members
條款7:為記憶體不足的狀況預做準備
條款8:撰寫 operator new 和 operator delete 時應遵行公約
條款9:避免遮掩了 new 的正規型式
條款10:如果你寫了一個 operator new,請對應也寫一個 operator delete

第三章:建構式,解構式,和 Assignment 運算子
條款11:如果 classes 內動態配置有記憶體,請為此 class 宣告一個 copy constructor 和一個 assignment 運算子
條款12:在 constructor 中儘量以 initialization 取代 assignment
條款13:Initialization list 中的 members 初始化排列次序應該和其在 class 內的宣告次序相同
條款14:總是讓 base class 擁有 virtual destructor
條款15:令 operator= 傳回「*this 的 reference」
條款16:在 operator= 中為所有的 data members 指派內容
條款17:在 operator= 中檢查是否「自己派給自己」

第四章:類別與函式之設計和宣告
條款18:努力讓介面完滿且最小化
條款19:區分 member functions, non-member functions 和 friend functions 三者
條款20:避免將 data members 放在公開介面中
條款21:儘可能使用 const
條款22:儘量使用 pass-by-reference(傳址),少用 pass-by-value(傳值)
條款23:當你必須傳回 object 時,不要嘗試傳回 reference
條款24:在函式多載化(function overloading)和參數預設化(parameter defaulting)之間,謹慎抉擇
條款25:避免對指標型別和數值型別進行多載化
條款26:防衛潛伏的 ambiguity(模稜兩可)狀態
條款27:如果不想使用編譯器暗自產生的 member functions,就應該明白拒絕它
條款28:嘗試切割 global namespace(全域命名空間)

第五章:類別與函式之實作
條款29:避免傳回內部資料的 handles
條款30:避免寫出 member function,傳回一個 non-const pointer 或 reference 並以之指向較低存取層級的 members
條款31:千萬不要傳回「函式內 local 物件的 reference」,或是「函式內以 new 獲得的指標所指的物件」
條款32:儘可能延緩變數定義式的出現
條款33:明智地運用 inlining
條款34:將檔案之間的編譯依存關係(compilation dependencies)降至最低

第六章:繼承機制與物件導向設計
條款35:確定你的 public inheritance 模塑出 "isa" 的關係
條款36:區分「介面繼承(interface inheritance)」和「實作繼承(implementation inheritance)」
條款37:絕對不要重新定義一個繼承而來的非虛擬函式
條款38:絕對不要重新定義一個繼承而來的預設參數值
條款39:避免在繼承體系中做 cast down(向下轉型)的動作
條款40:透過 layering(分層技術)來模塑 has-a 或 is-implemented-in-terms-of 的關係
條款41:區分 inheritance 和 templates
條款42:明智地運用 private inheritance(私有繼承)
條款43:明智地運用多重繼承(multiple inheritance,MI)
條款44:說出你的意思並瞭解你所說的每一句話

第七章:雜項討論(Miscellany)
條款45:知道 C++(編譯器)默默為我們完成和呼叫哪些函式
條款46:寧願編譯和聯結時出錯,也不要執行時才錯
條款47:使用 non-local static objects 之前先確定它已有初值
條款48:不要對編譯器的警告訊息視如不見
條款49:儘量讓自己熟悉 C++ 標準程式庫
條款50:加強自己對 C++ 的瞭解


more-effective-cpp.jpg (30509 bytes)

【基本資料】
書名:More Effective C++ : 35 New Ways to Improve Your Programs and Designs
作者:Scott Meyers
出版:Addison Wesley, 0-201-63371-X, 1996
定價:未標示
頁數:318

繼 Effective C++ 之後,Scott Meyers 於 1996 推出這本「續集」。條款變得比較少,頁數倒是多了一些,原因是這次選材比「第一集」更高階,尤其是第五章。Meyers 將此章命名為技術(Techniques),並明白告訴你,其中都是一些 patterns,例如 virtual ctors、smart pointers、reference counting、proxy classes,double dispatching…等等。這一章的每個條款篇幅都在 15~30 頁之譜,實在讓人有「山窮水盡疑無路,柳暗花明又一村」之嘆。

雖然出版年代稍嫌久遠,本書並沒有第二版,原因是當其出版之時(1996),C++ Standard 已經幾乎定案,本書即依當時的標準草案而寫。其間與現今之 C++ 標準規格幾乎相同。可能變化的幾個彈性之處,Meyers 也都有所說明與提示。讀者可以連結作者提供的網址,看看上下兩集的勘誤與討論(數量之多,令人驚恐。幸好多是技術討論或文字斟酌,並沒有什麼重大誤失)。

本書第一頁列有它所獲得的讚譽。對於讀者,這是一份值得參考的資料,我試著把它們譯為中文,條列於下:

※這是一本多方面發人深省的 C++ 書籍:不論在你偶爾用到的語言特性上,或是在你自以為十分熟悉的語言特性上。只有深刻瞭解 C++ 編譯器如何解釋你的碼,你才有可能以 C++ 語言寫出穩健強固的軟體。本書是協助你獲得此等層級之瞭解過程中,一份極具價值的資源。讀過本書之後,我感覺像是瀏覽了 C++ 程式大師所檢閱過的碼,並獲得許多極具價值的洞見。
- Fred Wild, Vce President of Technology,
Advantage Software echnologies

※本書內含大量重要的技術,這些技術是撰寫優良 C++ 程式所不可或缺的。本書解釋如何設計和實作這些觀念,以及潛伏在其他某些替代方案中的陷阱。本書亦含晚近加入之 C++ 特性的詳細說明。任何人如果想要好好地運用這些新特性,最好買一本並且放在隨手可得之處,以備查閱。
- Chrisopher J. Van Wyk, Professor
Mahematics and Computer Science, Drew University

※這是一本具備工業強度的最佳書籍。對於已經閱讀過 Effetive C++ 的人,這是完美的續集。
- Eric Nagler, ++ Instructor and Author,
Univesity of California Santa Cruz Extension

※More Effective C++ 是一本無微不至而且價值不扉的書籍,是 Scott 第一本書 Effective C++ 的續集。我相信每一位專業的 C++ 軟體開發人員都應該讀過並記憶 Effective C++ 和 More Effective C++ 兩本書內的各種招式,以及其中重要(並且有時候不可思議)的語言面向。我強烈推薦這兩本書給軟體開發人員、測試人員、管理人員…,每個人都可以從 Scott 專家級的知識與卓越的表達能力中獲益。
- Steve Burkett, Software Consutant


以下列出 More Effective C++ 的各個條款(我試著譯為中文),一方面讓這篇書評的實用性更高一些,一方面就讓這些條款標題直接彰顯它們的價值吧。

第一章:基礎議題(Basics)
條款1:仔細區別 pointers 和 references
條款2:最好使用 C++ 轉型運算子
條款3:絕對不要以 polymorphically(多型)方式來處理陣列
條款4:非必要不使用 default constructor

第二章:運算子(Operators)
條款5:對自定的型別轉換函式保持警覺
條款6:區別 increment/decrement 運算子的前序(prefix)和後序(postfix)型式
條款7:千萬不要多載化 &&, ||, 和 , 運算子
條款8:瞭解各種不同意義的 new 和 delete

第三章:異常情況(Exceptions)
條款9:利用 destructors 避免遺失資源
條款10:在 constructors 內阻止資源的遺失(resource leaks)
條款11:禁止異常訊息(exceptions)流出 destructors 之外
條款12:瞭解「丟出一個 exception」與「傳遞一個參數」或「呼叫一個虛擬函式」之間的差異
條款13:以 by reference 方式捕捉 exceptions
條款14:明智運用 exception specifications
條款15:瞭解異常處理(exception handling)的成本

第四章:效率(Efficiency)
條款16:謹記 80-20 法則
條款17:考慮使用 lazy evaluation
條款18:分期攤還預期的計算成本
條款19:瞭解暫時性物件的來源
條款20:協助完成「傳回值最佳化(RVO)」
條款21:利用多載化技術(overload)避免隱式型別轉換
條款22:考慮以運算子的複合型式(op=)取代其獨身型式(op)
條款23:考慮使用其他程式庫
條款24:瞭解 virtual functions、multiple inheritance、virtual base classes、runtime type identification 所需的成本

第五章:技術(Techniques,又稱 Idioms 或 Pattern)
條款25:將 constructor 和 non-member functions 虛擬化
條款26:限制某個 class 所能產生的物件數量
條款27:要求(或禁止)物件產生於 heap 之中
條款28:Smart Pointers(精靈指標)
條款29:Reference counting(參用計數)
條款30:Proxy classes(替身類別、代理人類別)
條款31:讓函式根據一個以上的物件型別來決定如何虛擬化

第六章:雜項討論(Miscellany)
條款32:在未來時態下發展程式
條款33:將非尾端類別(non-leaf classes)設計為抽象類別(abstract classes)
條款34:如何在同一個程式中結合 C++ 和 C
條款35:讓自己習慣使用標準的 C++ 語言


exceptional-Cpp.jpg (33071 bytes)

【基本資料】
書名:Exceptional C++ : 47 Engineering Puzzles, Programming Problems, and Solutions
作者:Herb Sutter
出版:Addison Wesley, 0-201-61562-2, 1999
定價:US$ 33.95
頁數:206

相較於前兩本威名遠播的老大哥,這本小書不但資歷淺,作者也似乎不是那麼有名。真的不那麼有名嗎?未必,這要看你熟悉哪個領域,在哪個領域活動而定。本書內容源自極受歡迎的 C++ 網際網路節目 Guru of the Week,作者Herb Sutter 正是這個網路節目的創辦者,同時也是C++ Report 的專欄作家,以及網際網路討論群 comp.lang.c++.moderated 的創始主持人。

本書的一個最大特色是,以實例進行的方式,告訴你如何以標準 C++ 進行軟體工程。書中所列的實例,有些是棘手的 C++/OOP 疑難雜症,有些展示穩健強固且具擴充性的程式碼。只需數分鐘時間,你便可以拿書中一些 C++ 設計問題和實作問題來挑戰自己並且獲得樂趣(是的,每個問題都有一個難度評分)。

每一個實例,都可以測驗你的觀念正確與否。書中許多主題與Effective C++ 和 More Effective C++ 的條款相互輝映,一個實例之中可能隱含了 Meyers 兩本書的數個條款的運用。順帶一提,這本書有老大哥 Scott Meyers 寫序推薦。

以下列出 Exceptional C++ 的各個條款(我試著譯為中文),Sutter 下標題的方式不像 Meyers 那麼具有實用性。不過,這些標題大略呈現了本書的方向,所以,還是讓它們自我說明吧。

第一章:泛型程式設計與 C++ 標準程式庫
條款1:Iterators(泛型指標)
條款2:無分大小寫的字串 之一
條款3:無分大小寫的字串 之二
條款4:將泛型容器(Generic Containers)的重用性擴張至最大 之一
條款5:將泛型容器(Generic Containers)的重用性擴張至最大 之二
條款6:暫時物件
條款7:使用標準程式庫(再談暫時物件)

第二章:Exception-Safety(異常情況下仍然安全)的主題與相關技術
條款8: 撰寫 Exception-Safe 程式碼 之一
條款9: 撰寫 Exception-Safe 程式碼 之二
條款10:撰寫 Exception-Safe 程式碼 之三
條款11:撰寫 Exception-Safe 程式碼 之四
條款12:撰寫 Exception-Safe 程式碼 之五
條款13:撰寫 Exception-Safe 程式碼 之六
條款14:撰寫 Exception-Safe 程式碼 之七
條款15:撰寫 Exception-Safe 程式碼 之八
條款16:撰寫 Exception-Safe 程式碼 之九
條款17:撰寫 Exception-Safe 程式碼 之十
條款18:程式碼複雜度 之一
條款19:程式碼複雜度 之二

第三章:Class 的設計與繼承
條款20:Class 技術
條款21:改寫(Overriding)虛擬函式
條款22:Classes 之間的關係 之一
條款23:Classes 之間的關係 之二
條款24:使用/禁用 繼承(Inheritance)
條款25:物件導向程式設計

第四章:編譯器防火牆(Firewalls)及 Pimpl Idiom
條款26:將編譯時期的依存性最小化 之一
條款27:將編譯時期的依存性最小化 之二
條款28:將編譯時期的依存性最小化 之三
條款29:編譯防火牆(Compilation Firewalls)
條款30:The "Fast Pimpl" Idiom

第五章:名稱查詢(Name Lookup)、命名空間(Namespaes)、介面準則(Interface Principle)
條款31:名稱查詢與介面準則 之一
條款32:名稱查詢與介面準則 之二
條款33:名稱查詢與介面準則 之三
條款34:名稱查詢與介面準則 之四

第六章:記憶體管理
條款 35:記憶體管理 之一
條款 36:記憶體管理 之二
條款 37:auto_ptr

第七章:陷阱、易犯錯誤與反常作法
條款 38:物件識別(Object Identity)
條款 39:自動轉換(Automatic Conversions)
條款 40:物件壽命(Object Lifetimes)之一
條款 41:物件壽命(Object Lifetimes)之二

第八章:雜項主題
條款 42:變數的初始化
條款 43:常數性的正確性(Const-Correctness)
條款 44:轉型(Casts)
條款 45:真假值(bool)
條款 46:轉呼叫(Forwarding)函式
條款 47:控制流程(Control Flow)


●結語

理論與實務的結合,是每一位資訊從業人員希望達到的目標。過多的照本宣科,過少的實務經驗(真正有價值的實務經驗),卻是目前資訊相關書籍的普遍寫照。

Effective C++,More Effective C++, 和 Exceptional C++,這三本書不僅在實務面上提出極為寶貴的準則,並以豐富而紮實的理論基礎,深入解釋每一條準則背後的來龍去脈。可以相當程度地提昇你的 C++ 程式技術與 OO 設計思維。

著名的語言專家 Benjamin Whorf 曾說:『語言可以塑造人類思考的方式,並決定人類思考的內容』。使用 C++ 語言,絕不僅只於使用 C++ 編譯器(很多人確是如此 — 雖然他們不自覺);重要的是運用隱含於語意之中的物件導向(Object Oriented)觀念與泛型(Genericity)觀念,在軟體設計階段便創造出一個有彈性、易修改、適用未來時態的架構。

對於物件導向領域的生手,這三本書可以給你一個震憾教育,讓你知道過去的寫作是多麼地不嚴謹、觀念是多麼地不成熟。或許這套書會促使很多自以為是的讀者謙卑地認知自我的不足,並謙卑地終於知道什麼是井蛙、河魚和燕雀。

至於物件導向箇中老手,是的,這三本書為你整理出多年打滾所獲的寶貴心得,佐以深度的學理探討,必然讓你有相見恨晚之嘆。

-- the end