內容簡介
作者Lippman參與設計了全世界第一套C++編譯程式cfront,這本書就是一位偉大的C++編譯程式設計者向你闡述他如何處理各種explicit(明確出現於C++程式代碼中)和implicit(隱藏於程式代碼背後)的C++語意。
本書專注於C++面向對象程式設計的底層機制,包括結構式語意、臨時性對象的生成、封裝、繼承,以及虛擬——虛擬函式和虛擬繼承。這本書讓你知道:一旦你能夠了解底層實現模型,你的程式代碼將獲得多么大的效率。Lippman澄清了那些關於C++額外負荷與複雜度的各種錯誤信息和迷思,但也指出其中某些成本和利益交換確實存在。他闡述了各式各樣的實現模型,指出它們的進化之道及其本質因素。書中涵蓋了C++對象模型的語意暗示,並指出這個模型是如何影響你的程式的。
對於C++底層機制感興趣的讀者,這必然是一本讓你大呼過癮的絕妙好書。如果你是一位C++程式設計師,渴望對於底層知識獲得一個完整的了解,那么本書正適合你
譯 序
本立道生
對於傳統的結構化(sequential)語言,我們向來沒有太多的疑惑,雖然在函式調用的背後,也有著堆疊建制、參數排列、返回地址、堆疊清除等等幕後機制,但函式調用是那么的自然而明顯,好像只是夾帶著一個包裹,從程式的某一個地點跳到另一個地點去執行。
但是對於面向對象(Object Oriented)語言,我們的疑惑就多了。究其因,這種語言的編譯器為我們(程式設計師)做了太多的服務:構造函式、析構函式、虛擬函式、繼承、多態……有時候它為我們合成出一些額外的函式(或運算符),有時候它又擴張我們所寫的函式內容,放進更多的操作。有時候它還會為我們的objects加油添醋,放進一些奇妙的東西,使你面對sizeof的結果大驚失色。
我心裡頭一直有個疑惑:電腦程式最基礎的形式,總是脫離不了一行一行的循序執行模式,為什麼OO(面向對象)語言卻能夠“自動完成”這么多事情呢?另一個疑惑是,威力強大的polymorphism(多態),其底層機制究竟如何?
如果不了解編譯器對我們所寫的C++代碼做了什麼手腳,這些困惑永遠解不開。
這本書解決了過去令我百思不解的諸多疑惑。我要向所有已具備C++多年程式設計經驗的同好們大力推薦這本書。
這本書同時也是躍向組件軟體(component-ware)基本精神的“跳板”。不管你想學習COM(Component Object Model)、CORBA(Common Object Request Broker Architecture)或是SOM(System Object Model),了解C++ Object Model,將使你更清楚軟體組件(components)設計上的難點與運用之道。不但我自己在學習COM的道路上有此強烈的感受,Essential COM(《COM本質論》,侯捷譯,碁峰1998)的作者Don Box也在他的書中推崇Lippman的這一本卓越的書籍。
是的,這當然不會是一本輕鬆的書籍。某些章節(例如3、4兩章)可能給你立即的享受——享受於面對底層機制有所體會與掌控的快樂;某些章節(例如5、6、7三章)可能帶給你短暫的痛苦——痛苦於艱難深澀、難以吞咽的內容。這些快樂與痛苦,其實就是我翻譯此書時的心情寫照。無論如何,我希望通過我的譯筆,把這本難得的好書帶到更多人面前,引領大家見識C++底層建設的技術之美。
侯捷 2011.10.20 於新竹
請注意:本書特點,作者Lippman在其前言中有很詳細的描述,我不再多言。翻譯用詞與心得,記錄在第0章(譯者的話)之中,對您或有導讀之功。
請注意:原文本有大大小小約80~90個筆誤。有的無傷大雅,有的影響閱讀順暢甚巨(如前後文所用符號不一致、內文與圖形所用符號不一致——甚至因而導致圖片的文字解釋不正確)。我已在第0章(譯者的話)列出所有我找到的錯誤。此外,某些場合我還會在錯誤出現之處再加注,表示原文內容為何。這么做不是畫蛇添足,也不為彰顯什麼。我知道有些讀者拿著原文書和中譯書對照著看,我把原書錯誤加注出來,可免讀者懷疑是否我打錯字或是譯錯了。另一方面也是為了文責自負……唔……萬一Lippman是對的而J.J.Hou錯了呢?!我雖有相當把握,但還是希望明白攤開來讓讀者檢驗。
目 錄
本立道生(侯捷 譯序) III
目錄 VII
前言(Stanley B. Lippman) XIII
第0章 導讀(譯者的話) XXV
第1章 關於對象(Object Lessons) 1
第2章 構造函式語意學(The Semantics of Constructors) 37
第3章 Data語意學(The Semantics of Data) 83
第4章 Function語意學(The Semantics of Function) 139
第5章 構造、析構、拷貝語意學(Semantics of Construction,
第6章 執行期語意學(Runtime Semantics) 237
第7章 站在對象模型的尖端(On the Cusp of the Object Model) 279
前 言
差不多有10年之久,我在貝爾實驗室(Bell Laboratories)埋首於C++的實現任務。最初的工作是在cfront上面(Bjarne Stroustrup的第一個C++編譯器),從1986年的1.1版到1991年9月的3.0版。然後移轉到Simplifier(這是我們內部的命名),也就是Foundation項目中的C++對象模型部分。在Simplifier設計期間,我開始醞釀本書。
Foundation項目是什麼?在 Bjarne 的領導下,貝爾實驗室中的一個小組探索著以C++完成大規模程式設計時的種種問題的解決之道。Foundation項目是我們為了構建大系統而努力定義的一個新的開發模型(我們只使用C++,並不提供多重語言的解決方案)。這是個令人興奮的工作,一方面是因為工作本身,一方面是因為工作夥伴:Bjarne、Andy Koenig、Rob Murray、Martin Carroll、Judy Ward、Steve Buroff、Peter Juhl,以及我自己。Barbara Moo管理我們這一群人(Bjarne和Andy除外)。Barbara Moo常說管理一個軟體團隊,就像放牧一群驕傲的貓。
我們把Foundation想像成一個核心,在那上面,其他人可以為使用者鋪設一層真正的開發環境,把它整修為他們所期望的UNIX或Smalltalk模型。私底下我們把它稱為Grail(傳說中耶穌最後晚餐所用的聖杯),人人都想要,但是從來沒人找到過!
Grail使用一個由Rob Murray發展出來並命名為ALF的面向對象層次結構,提供一個永久的、以語意為基礎的表現法。在Grail中,傳統編譯器被分解為數個各自分離的執行檔。parser負責建立程式的ALF表現法。其他每一個組件(如type checking、simplification、code generation)以及工具(如browser)都在程式的一個ALF表現體上操作(並可能加以擴展)。Simplifier是編譯器的一部分,處於type checking和code generation之間。Simplifier 這個名稱是由Bjarne所倡議的,它原本是cfront的一個階段(phase)。
在type checking和code generation之間,Simplifier做什麼事呢?它用來轉換內部的程式表現。有三種轉換風味是任何對象模型都需要的:
1.與編譯器息息相關的轉換(Implementation-dependent transformations)
這是與特定編譯器有關的轉換。在ALF之下,這意味著我們所謂的“tentative”nodes。例如,當parser看到這個表達式:
fct();
它並不知道是否(a)這是一個函式調用操作,或者(b)這是overloaded call operator在class object fct上的一種套用。默認情況下,這個式子所代表的是一個函式調用,但是當(b)的情況出現,Simplifier 就要重寫並調換 call subtree。
2.語言語意轉換(Language semantics transformations)
這包括constructor/destructor的合成和擴展、memberwise初始化、對於memberwise copy的支持、在程式代碼中安插conversion operators、臨時性對象,以及對constructor/destructor的調用。
3.程式代碼和對象模型的轉換(Code and object model transformations)
這包括對virtual functions、virtual base class和inheritance的一般支持、new和delete運算符、class objects所組成的數組、local static class instances、帶有非常量表達式(nonconstant expression)之global object的靜態初始化操作。我對Simplifier所規劃的一個目標是:提供一個對象模型體系,在其中,對象的實現是一個虛擬接口,支持各種對象模型。
最後兩種類型的轉換構成了本書的基礎。這意味著本書是為編譯器設計者而寫的嗎?不是,絕對不是!這本書是由一位編譯器設計者針對中高級C++程式設計師所寫的。隱藏在這本書背後的假設是,程式設計師如果了解C++對象模型,就可以寫出比較沒有錯誤傾向而且比較有效率的代碼。
什麼是C++對象模型
有兩個概念可以解釋C++對象模型:
1. 語言中直接支持面向對象程式設計的部分。
2. 對於各種支持的底層實現機制。
語言層面的支持,涵蓋於我的C++ Primer一書以及其他許多C++書籍當中。至於第二個概念,則幾乎不能夠於目前任何讀物中發現,只有[ELLIS90]和[STROUP94]勉強有一些蛛絲馬跡。本書主要專注於C++對象模型的第二個概念。本書語言遵循C++委員會於1995冬季會議中通過的Standard C++草案(除了某些細節,這份草案應該能夠反映出此語言的最終版本)。
C++對象模型的第一個概念是一種“不變數”。例如,C++ class的完整virtual functions在編譯時期就固定下來了,程式設計師沒有辦法在執行期動態增加或取代其中的某一個。這使得虛擬調用操作得以快速地派送(dispatch)結果,付出的成本則是執行期的彈性。
對象模型的底層實現機制,在語言層面上是看不出來的——雖然對象模型的語意本身可以使得某些實現品(編譯器)比其他實現品更接近自然。例如,virtual function calls,一般而言是通過一個表格(內含virtual functions地址)的索引而決議得知。一定要使用如此的 virtual table 嗎?不,編譯器可以自由引進其他任何變通做法。如果使用virtual table,那么其布局、存取方法、產生時機以及數百個細節也都必須決定下來,而所有決定也都由每一個實現品(編譯器)自行取捨。不過,既然說到這裡,我也必須明白告訴你,目前所有編譯器對於virtual function的實現法都是使用各個class專屬的virtual table,大小固定,並且在程式執行前就構造好了。
如果C++對象模型的底層機制並未標準化,那么你可能會問:何必探討它呢?主要的理由是,我的經驗告訴我,如果一個程式設計師了解底層實現模型,他就能夠寫出效率較高的代碼,自信心也比較高。一個人不應該用猜的方式,或是等待某大師的宣判,才確定“何時提供一個copy constructor而何時不需要”。這類問題的解答應該來自於我們自身對對象模型的了解。
寫本書的第二個理由是為了消除我們對於C++語言(及其對面向對象的支持)的各種錯誤認識。下面一段話節錄自我收到的一封信,來信者希望將C++引進於其程式環境中:
我和一群人工作,他們過去不曾寫過(或完全不熟悉)C++和OO。其中一位工程師從1985就開始寫C了,他非常強烈地認為C++只對那些user-type程式才好用,對server程式卻不理想。他說如果要寫一個快速而有效率的資料庫引擎,應該使用C而非C++。他認為C++龐大又遲緩。
C++當然並不是天生地龐大又遲緩,但我發現這似乎成為C程式設計師的一個共識。然而,光是這么說並不足以使人信服,何況我又被認為是C++的“代言人”。本書就是企圖極儘可能地將各式各樣的Object facilities(如inheritance、virtual functions、指向class members的指針……)所帶來的額外負荷說個清楚。
除了我個人回答這封信外,我也把此信轉寄給HP的Steve Vinoski;先前我曾與他討論過C++的效率問題。以下節錄自他的回應:
過去數年我聽過太多與你的同事類似的看法。許多情況下,這些看法是源於對C++事實真相的缺乏了解。在上周,我和一位朋友閒聊,他在一家IC製造廠服務,他說他們不使用C++,因為“它在你的背後做事情”。我連連追問,於是他說根據他的了解,C++調用malloc()和free()而不讓程式設計師知道。這當然不是真的。這是一種所謂的迷思與傳說,引導出類似於你的同事的看法……
在抽象性和實際性之間找出平衡點,需要知識、經驗以及許多思考。C++的使用需要付出許多心力,但是我的經驗告訴我,這項投資的回報率相當高。
我喜歡把本書想像成是我對那一封讀者來信的回答。是的,本書是一個知識陳列庫,幫助大家去除圍繞在C++四周的迷思與傳說。
如果C++對象模型的底層機制會因為實現品(編譯器)和時間的變動而不同,我如何能夠對於任何特定主題提供一般化的討論呢?靜態初始化(Static initialization)可為此提供一個有趣的例子。
已知一個class X有著constructor,如下面所示:
class X
{
friend istream&
operator>>( istream&, X& );
public:
X( int sz = 1024 ) { ptr = new char[ sz ]; }
...
private:
char *ptr;
};
而一個class X的global object的聲明,如下面所示:
X buf;
int main()
{
// buf 必須在這個時候構造起來
cin >> setw( 1024 ) >> buf;
...
}
C++對象模型保證,X constructor將在main()之前便把buf初始化。然而它並沒有說明這是如何辦到的。答案是所謂的靜態初始化(static initialization),實際做法則有賴開發環境對此的支持屬於哪一層級。
原始的cfront實現品不單只是假想沒有環境支持,它也假想沒有明確的目標平台。唯一能夠假想的平台就是UNIX及其衍化的一些變體。我們的解決之道也因此只專注在UNIX身上:我們使用nm命令。CC命令(一個UNIX shell script)產生出一個執行檔,然後我們把nm施行於其上,產生出一個新的.c檔案。然後編譯這個新的.c檔案,再重新連結出一個執行檔(這就是所謂的munch solution)。這種做法是以編譯器時間來交換移植性。
接下來是提供一個“平台特定”解決之道:直接驗證並穿越COFF-based程式的執行檔(此即所謂的 patch solution),不再需要nm、compile、relink。COFF 是Common Object File Format的縮寫,是System V pre-Release 4 UNIX系統所發展出來的格式。這兩種解決方案都屬於程式層面,也就是說,針對每一個需要靜態初始化的.c檔案,cfront 會產生出一個sti函式,執行必要的初始化操作。不論是patch solution還是munch solution,都會去尋找以sti開頭的函式,並且安排它們以一種未被定義的順序執行(由安插在main()之後第一行的一個library function _main()執行之)(譯註:本書第6章對此有詳細說明)。
System V COFF-specific C++編譯器與cfront的各個版本平行發展。由於瞄準了一個特定平台和特定作業系統,此編譯器因而能夠影響連結器特地為它修改:產生出一個新的.ini section,用以收集需要靜態初始化的objects。連結器的這種擴充方式,提供了所謂的environment-based solution,那當然更在program-based solution層次之上。
至此,任何以cfront program-based solution為基礎的一般化(泛型)操作將令人迷惑。為什麼?因為C++已經成為主流語言,它已經接收了越來越多的environment-based solutions。本書如何維護其間的平衡呢?我的策略如下:如果在不同的C++編譯器上有重大的實現技術差異,我就至少討論兩種做法。但如果 cfront之後的編譯器實現模型只是解決cfront原本就已理解的問題,例如對虛擬繼承的支持,那么我就闡述歷史的演化。當我說到“傳統模型”時,我的意思是Stroustrup的原始構想(反映在cfront身上),它提供一種實現模範,在今天所有的商業化實現品上仍然可見。
本書組織
第1章,關於對象(Object Lessons),提供以對象為基礎的觀念背景,以及由C++提供的面向對象程式設計範式(paradigm。譯註:關於paradigm這個字,請參閱本書第1章第22頁的譯註)。本章包括對對象模型的一個概述,說明目前普及的工業產品,但沒有對於多重繼承和虛擬繼承有太靠近的觀察(那是第3章和第4章的重頭戲)。
第2章,構造函式語意學(The Semantics of Constructors),詳細討論constructor 如何工作。本章談到constructors何時被編譯器合成,以及給你的程式效率帶來什麼樣的意義。
第3章至第5章是本書的重要內容。在這裡,我詳細地討論了C++對象模型的細節。第3章,Data語意學(The Semantics of Data),討論data members的處理。第4章,Function語意學(The Semantics of Function),專注於各式各樣的member functions,並特別詳細地討論如何支持virtual functions。第5章,構造、析構、拷貝語意學(Semantics of Construction, Destruction, and Copy),討論如何支持class模型,也討論到object的生命期。每一章都有測試程式以及測試數據。我們對效率的預測,將拿來和實際結果做比較。
第6章,執行期語意學(Runtime Semantics),查看執行期的某些對象模型行為。包括臨時性對象的生命及其死亡,以及對new運算符和delete運算符的支持。
第7章,在對象模型的尖端(On the Cusp of the Object Model),專注於exception handling、template support、runtime type identification。
預定的讀者
本書可以扮演家庭教師的角色,不過它定位在中級以上的C++程式設計師,而非C++新手。我嘗試提供足夠的內容,使它能夠被任何有點C++基礎(例如讀過我的C++ Primer並有一些實際編程經驗)的人接受。理想的讀者是,曾經有過數年的C++編程經驗,希望進一步了解“底層做些什麼事”的人。書中某些部分甚至對於C++高手也具有吸引力,如臨時性對象的產生,以及named return value(NRV)最佳化的細節等等。在與本書素材相同的各個公開演講場合中,我已經證實了這些材料的吸引力。
程式範例及其執行
本書的程式範例主要有兩個目的:
1. 為了提供書中所述C++對象模型各種概念的具體說明。
2. 提供測試,以測量各種語言性質的相對成本。
無論哪一種意圖,都只是為了展現對象模型。舉例而言,雖然我在書中有大量的舉例,但我並非建議一個真實的3D graphic library必須以虛擬繼承的方式來表現一個3D點(不過,你可以在[POKOR94]中發現作者Pokorny的確是這么做的)。
書中所有測試程式都在一部SGI Indigo2xL上編譯執行,使用SGI 5.2 UNIX作業系統中的CC和NCC編譯器。CC是cfront 3.0.1版(它會產生出C代碼,再由一個C編譯器重新編譯為執行檔)。NCC是Edison Design Group的C++ front-end 2.19版,內含一個由SGI供應的程式代碼產生器。至於時間測量,是採用UNIX的timex命令針對1 000萬次疊代測試所得的平均值。
雖然在xL機器上使用這兩個編譯器,對讀者而言可能覺得有些神秘,我卻覺得對此書的目的而言,很好。不論是cfront或現在的Edison Design Group's C++ front-end(Bjarne稱其為“cfront的兒子”),都與平台無關。它們是一種一般化的編譯器,被授權給34家以上的計算機製造商(其中包括Gray、SGI、Intel)和軟體開發環境廠商(包括Centerline和Novell,後者是原先的UNIX軟體實驗室)。效率的測量並非為了對目前市面上的各家編譯系統做評比,而只是為了提供C++對象模型之各種特性的一個相對成本測量。至於商業評比的效率數據,你可以在幾乎任何一本計算機雜誌的計算機產品檢驗報告中獲得。
致謝
略
參考書目
略
第0章 導讀(譯者的話)
讀者對象
很不容易三言兩語就說明此書的適當讀者。作者Lippman參與設計了全世界第一套C++編譯器cfront,本書就是一位偉大的C++編譯器設計者向你闡述他如何處理各種explicit(明確出現於C++程式代碼)和implicit(隱藏於程式代碼背後)的C++語意。
對於C++程式老手,這必然是一本讓你大呼過癮的絕妙好書。
C++老手分兩類:一種人把語言用得爛熟,OO觀念也有;另一種人不但如此,還對於台面下的機制,如編譯器合成的default constructor、object的記憶體布局等有莫大的興趣。本書對於第二類老手的吸引力自不待言;至於第一類老手,或許你沒那么大的刨根究底的興趣,不過我還是非常推薦你閱讀此書。了解C++對象模型,絕對有助於你在語言本身以及面向對象觀念兩方面的層次提升。
你需要細細推敲每一個句子、每一個例子,囫圇吞棗是完全沒有用的。作者是C++大師級人物,並且參與開發了第一套C++編譯器,他的解說以及詮釋鞭辟入裡,你務必在看過每一小段之後,融會貫通,把思想觀念化為己有,再接續另一小節。但閱讀順序並不需要按照書中的章節排列。
閱讀順序
我個人認為,第1、3、4章最能帶給讀者迅速而最大的幫助,這些都是經常引起程式設計師困惑的主題。作者在這些章節中有不少示意圖(我自己也加了不少)。你或許可以從這三章挑著看起。
其他章節比較晦澀一些(我的感覺),不妨“視可而擇之”。
當然,這都是十分主觀的認定。客觀的意見只有一個:你可以隨你的興趣與需求,從任一章開始看起。各章之間沒有必然關聯性。
翻譯風格
太多朋友告訴我,他們閱讀中文計算機書籍,不論是著作或譯作,最大的閱讀困難在於一大堆沒有標準譯名的技術名詞或習慣用語(至於那些誤謬不知所云的奇怪作品當然本就不在考慮之列)。其實,就算國家相關機構指定了統一譯名(或曾有過,誰知道?),流通於工業界與學術界之間的還是原文名詞與術語。
對於工程師,我希望我所寫的書和我所譯的書能夠讓各位讀來通體順暢;對於學生,我還希望多發揮一點引導的力量,引導各位多使用、多認識原文術語和專有名詞,不要說出像“無模式對話盒(modeless dialog)”這種奇怪的話。
由於本書讀者定位之故,我決定保留大量的原文技術名詞與術語。我清楚地知道,在我們的技術領域裡,研究人員或工程師如何使用這些語彙。
當然,有些中文譯名套用較普遍,也較貼切,我並不排除使用。其間的挑選與決定,不可避免地帶了點個人色彩。
下面是本書出現的原文名詞(按字母排序)及其意義:
英文名詞 中文名詞或(及)其意義
access level 訪問級,存取級。就是C++的public、private、protected三種等級
access section 訪問區段,存取區段。就是class中的public、private、protected三種段落
alignment 邊界調整,調整至某些bytes的倍數。其結果視不同的機器而定。例如32位機器通常調整至4的倍數
bind 綁定,將程式中的某個符號真正附著(決議)至一塊實例上
chain 串鏈
class 類
class hierarchy class體系,class層次結構
composition 組合。通常與繼承(inheritance)一同討論
concrete inheritance 具體繼承(相對於抽象繼承)
constructor 構造函式
data member 數據成員(或被稱為member variable)
declaration,declare 聲明
definition,define 定義(通常附帶“在記憶體中挖一塊空間”的行為)
derived 派生
destructor 析構函式
encapsulation 封裝
explicit 顯式的(通常指C++程式代碼中明確出現的)
hierarchy 體系,層次結構
implement 實現(動詞)
implementation 實現品、實現物。本書有時候指C++編譯器。大部分時候是指class member function的內容
implicit 隱式的、暗喻的(通常指未出現在C++程式代碼中的)
inheritance 繼承
inline 內聯(C++的一個關鍵字)
instance 實例(有些書籍譯為“案例”,極不妥當)
layout 布局。本書常常出現這個字,意指object在記憶體中的數據分布情況
mangle 名稱切割重組(C++對於函式名稱的一種處理方式)
member function 成員函式。或被稱為function member
members 成員,泛指data members和member functions
object 對象(根據class的聲明而完成的一份占有記憶體的實例)
offset 偏移位置
operand 運算元
operator 運算符
overhead 額外負擔(因某種設計,而導致的額外成本)
overload 重載
overloaded function 多載函式
override 改寫(對virtual function的重新設計)
paradigm 範式(請參考第1章第22頁)
pointer 指針
polymorphism 多態(“面向對象”最重要的一個性質)
programming 程式設計、程式化
reference 參考、引用(動詞)
reference C++的&運算符所代表的東西。當做名詞解
resolve 決議。函式調用時連結器所進行的一種操作,將符號與函式實例產生關聯。如果你調用func()而連結時找不到func()實例,就會出現“unresolved externals”連結錯誤
slot 表格中的一格(一個元素),條孔,條目,條格
subtype 子類型
type 類型,類別(指的是int、float等內建類型,或C++ classes等自定類型)
virtual 虛擬
virtual function 虛擬函式
virtual inheritance 虛擬繼承
virtual table 虛擬表格(為實現虛擬機制而設計的一種表格,內放virtual functions的地址)
有時候考慮到上下文的因素,面對同一個名詞,在譯與不譯之間,我可能會有不同的選擇。例如,面對“pointer”,我會譯為“指針”,但由於我並未將reference譯為“參考”(實在不對味),所以如果原文是“the manipulation of a pointer or reference in C++……”,為了中英對等或平衡的緣故,我不會把它譯為“C++中對於指針和reference的操作行為……”,我會譯為“C++中對於pointer和reference的操作行為……”。
譯註
書中有一些譯註。大部分譯註,如果比較短的話,會被我直接放在括弧之中,接續本文。較長的譯註,則被我安排在被注文字的段落下面(緊臨,並加標示)。
原書錯誤
這本書雖說質地極佳,但製作的嚴謹度卻不及格!有損Lippman的大師地位。
屬於“作者筆誤”之類的錯誤,比較無傷大雅,例如少了一個;符號,或是多了一個;符號,或是少了一個}符號,或是多了一個)符號,等等。比較嚴重的錯誤,是程式代碼變數名稱或函式名稱或class名稱與文字敘述不一致,甚或是圖片中對於object布局的畫法,與程式代碼中的聲明不一致。這兩種錯誤都會嚴重耗費讀者的心神。
只要是被我發現的錯誤,都已被我修正。以下是錯誤更正列表。
示例:L5表示第5行,L-9表示倒數第9行。頁碼所示為原書頁碼。
頁碼 原文位置 原文內容 應修改為
p.35 最後一行 Bashful(), Bashful();
p.57 表格第二行 1.32.36 1:32.36
p.61 L1 memcpy...程式代碼最後少了一個)
p.61 L10 Shape()...程式代碼最後少了一個}
p.64 L-9 程式代碼最後多了一個 ;
p.78 最後四行碼 == 似乎應為 =
p.84 圖3.1b說明 struct Point3d class Point3d
p.87 L-2 virtual...程式代碼最後少了一個;
p.87 全頁多處 pc2_2(不符合命名意義) pc1_2(符合命名意義)
p.90 圖3.2a說明 Vptr placement and end of class Vptr placement at end of class
p.91 圖3.2b __vptr__has_vrts __vptr__has_virts
p.92 碼L-7 class Vertex2d class Vertex3d
p.92 碼L-6 public Point2d public Point3d
p.93 圖3.4說明 Vertex2d的對象布局 Vertex3d的對象布局
p.92~p.94 符號名稱混亂,前後誤謬不符 已全部更改過
p.97 碼L2 public Point3d, public Vertex 配合圖3.5a和圖3.5b,應調整順序為public Vertex, public Point3d
p.99 圖3.5a 符號與書中程式代碼多處不符 已全部更改過
p.100 圖3.5b 符號與書中程式代碼多處不符 已全部更改過
p.100 L-2 ? pv3d + ...最後多了一個)
p.106 L16 pt1d::y pt2d::_y
p.107 L10 & 3d_point::z; &Point3d::z;
p.108 L6 & 3d_point::z; &Point3d::z;
p.108 L-6 int d::*dmp, d *pd int Derived::*dmp, Derived *pd
p.109 L1 d *pd Derived *pd
p.109 L4 int b2::*bmp = &b2::val2; int Base2::*bmp = &Base2::val2;
p.110 L2 不符合稍早出現的程式代碼 把pt3d改為Point3d
p.115 L1 magnitude() magnitude3d()
p.126 L12 Point2d pt2d = new Point2d; ptr = new Point2d;
p.136 圖4.2右下 Derived::~close() Derived::close()
p.138 L-12 class Point3d...最後少了一個{
p.140 程式代碼 沒有與文字中的class命名一致 所有的pt3d改為Point3d
p.142 L-7 if ( this ...程式代碼最右邊少了一個)
p.143 程式代碼 沒有與文字中的class命名一致 所有的pt3d改為Point3d
p.145 L-6 pointer::z() Point::z()
p.147 L1 pointer::*pmf Point::*pmf
p.147 L5 point::x() Point::x()
p.147 L6 point::z() Point::z()
p.147 中段碼L-1 程式代碼最後缺少一個)
p.148 中段碼L1 (ptr->*pmf)函式最後少了一個;
p.148 中段碼L-1 (*ptr->vptr[..函式最後少了一個)
p.150 程式代碼 沒有與文字中的class命名一致 所有的pt3d改為Point3d
p.150 L-7 pA.__vptr__pt3d...最後少了一個;
p.152 L4 point new_pt; Point new_pt;
p.156 L7 { }
p.160 L11, L12 Abstract_Base Abstract_base
p.162 L-3 Abstract_base函式最後少了一個;
p.166 中,碼L3 Point1 local1 = ... Point local1 = ...
p.166 中,碼 L4 Point2 local2; Point local2;
p.174 中,碼L-1 Line::Line()函式最後多了一個;
p.174 中下,碼L-1 Line::Line()函式最後多了一個;
p.175 中上,碼L-1 Line::~Line()函式最後多了一個;
p.182 中下,碼L6 Point3d::Point3d() PVertex::PVertex()
p.183 上,碼L9 Point3d::Point3d() PVertex::PVertex()
p.185 上,碼L3 y = 0.0之前缺少float
p.186 中下,碼L6 缺少一個return
p.187 中,碼L3 const Point3d &p const Point3d &p3d
p.204 下,碼L3 缺少一個return 1;
p.208 中下,碼L2 new Pvertex; new PVertex;
p.219 上,碼L1 __nw(5*sizeof(int)); __new(5*sizeof(int));
p.222 上,碼L8 // new ( ptr_array...程式代碼少了一個;
p.224 中,碼L1 Point2w ptw = ... Point2w *ptw = ...
p.224 下,碼L5 operator new()函式定義多了一個;
p.225 上,碼L2 Point2w ptw = ... Point2w *ptw = ...
p.226 下,碼L1 Point2w p2w = ... Point2w *p2w = ...
p.229 中,碼L1 c.operator==( a + b ); c.operator=( a + b );
p.232 中下,碼L2 x xx; X xx;
p.232 中下,碼L3 x yy; X yy;
p.232 下,碼L2 struct x _1xx; struct X _1xx;
p.232 下,碼L3 struct x _1yy; struct X _1yy;
p.233 碼L2 struct x __0__Q1; struct X __0__Q1;
p.233 碼L3 struct x __0__Q2; struct X __0__Q2;
p.233 中,碼 if條件句的最後多了一個;
p.253 碼L-1 foo()函式最後多了一個;
推薦
我個人翻譯過不少書籍,每一本都精挑細選後才動手。(品質不夠的原文書,譯它做啥?!)在這些譯本當中,我從來不做直接而露骨的推薦。好的書籍自然而然會得到識者的欣賞。過去我譯的那些明顯具有實用價值的書籍,總有相當數量的讀者有強烈的需求,所以我從不擔心沒有足夠的人來為好書散播口碑。但Lippman的這本書不一樣,它可能不會為你帶來明顯而立即的實用性,它可能因此在書店中蒙上一層灰(其原文書我就沒聽說多少人讀過),枉費我從眾多原文書中挑出這本好書。我擔心聽到這樣的話:
對象模型?呵,我會寫C++程式,寫得一級棒,這些屬於編譯器層面的東西,於我何有哉!
對象模型是深層結構的知識,關係到“與語言無關、與平台無關、跨網路可執行”軟體組件(software component)的基礎原理。也因此,了解C++對象模型,是學習目前軟體組件三大規格(COM、CORBA、SOM)的技術基礎。
如果你對軟體組件(software component)沒有興趣,C++對象模型也能夠使你對虛擬函式、虛擬繼承、虛擬接口有脫胎換骨的新認知,或是對於各種C++寫法所帶來的效率利益有通盤的認識。
我因此要大聲地說:有經驗的C++ programmer都應該看看這本書。
如果你對COM有興趣,我也要同時推薦你看另一本書:Essential COM,Don Box著,Addison Wesley公司1998年出版(《COM本質論》,侯捷譯,碁峰1998)。這也是一本論述非常清楚的書籍,把COM的由來(為什麼需要COM、如何使用COM)以循序漸進的方式闡述得非常深刻,是我所看過的最理想的一本COM基礎書籍。
看Essential COM之前,你最好有這本Inside The C++ Object Model的基礎。