C++編程思想 特色及評論
本書作者根據自己學習C++的親身體會及多年教學經驗,用簡單的例子和簡練的敘述講解C++編程,別具特色。全書共分十八章,內容涉及對象的演化、數據抽象、隱藏實現、初始化與清除、函式重載與預設參數、輸入輸出流介紹。C++編程思想 內容簡介
全書共分十八章,內容涉及對象的演化、數據抽象、隱藏實現、初始化與清除、函式重載瑟預設參數、輸入輸出流介紹、常量、內聯函式、命名控制、引用和拷貝構造函式、運算符重載、動態對象創建、繼承和組合、多態和虛函式、模板和包容器類、多重繼承、異常處理和運行時類型識別。C++編程思想 本書前言
前 言與任何人類語言一樣,C++提供了一種表達思想的方法。如果這種表達方法是成功的,那
么當程式變得更大和更複雜時,該方法應當明顯地表現出比其他語言更容易和更靈活等優點。
不能只把C++看作是一組性能的匯集,因為有些性能不能單獨使用。如果我們不只是用這
種語言編寫代碼,而是用它思考“設計”問題,那么就能綜合使用這些性能。而且,為了用這
種方法理解C++, 我們必須首先掌握C和一般的編程問題。這本書討論的是編程問題,討論為
什麼它是問題以及用C++解決編程問題所採用的方法。因此,在每一章中所解釋的一組性能,
都是建立在我用這種語言解決一類特殊問題所採用方法的基礎上的。用這種方法,我希望一點
一點地引導讀者,從掌握C開始,直到使C++在讀者的頭腦中成為他自己的語言。
我將始終堅持一種觀點:讀者應當在頭腦中建立一個模型,以便於理解這個語言,直到爐
火純青。如果遇到難題,他可以將問題納入這個模型,推導出答案。我將努力把已經印在我腦
海中的見解傳授給讀者,正是這些見解,使我能開始“用C++思考”。
1.預備知識要求
在我的第一本C++書[1]中,我把C和C++放在一起講解,很多人說他們喜歡這樣,但我發
現,現在已經有了許多關於C語言的好的指南和好的教師。我覺得,如果讀者已經學過C, 並
且至少能用它順利地讀程式,那么這本書就會更有用。在本書中,我將致力於解決C++中的難
點。另一方面,就像能在小說中通過上下文直觀地學習許多新名詞一樣,從本書的文字中讀者
也可以學習到大量關於C的知識。
我還發現,我沒有耐心講授同時覆蓋C和C++的課程。我能保持三天的高度積極性和旺盛
精力,然後就開始低落。似乎也就在這時,聽眾的頭腦也裝滿了,所以這也就是我的小型培訓
研討班的期限。對於大型班,我們根本不可能完成練習,在當前材料的情況下,我可以在2~3
天的教學中涵蓋大部分內容。
我盡力不用任何特定廠商的C++版本,因為對於學習語言,我不認為特定實現的細節像語
言本身一樣重要。而大部分廠商的文檔適合於他們自己的特定實現。
2.學習C++
我希望這本書的讀者有和我進入C++時相同的情況,因為一個C程式設計師對於編程持有實在
而執著的態度。但糟糕的是,我的背景和經驗是在硬體層的嵌入式編程方面。在那裡,C常常
被看作高層語言,它對於位操作是低效率的。後來我發現,自己甚至不是一個好的C程式設計師,
平時掩蓋著對malloc()&free()、setjmp()&longjmp()結構和其他“複雜”概念的無知,開始觸
及這些主題時就迴避了,而不是努力去獲取新的知識。
在我開始致力於學習C++時,當時唯一像樣的書是Stroustrup的“自學專家指南”[2],因此
我只好自己消化基本概念。這引出了我的第一本C++書[3],基本上就是直接把我頭腦中的經驗
倒出來而寫成的。它被用來作為讀者指南,引導程式設計師同時進入C和C++這本書的兩個版本[1]
都得到了積極的反響,我至今仍然認為它是有價值的。
幾乎就在《UsingC++》出版的同時 我開始講授這門語言。講授C++已經變成了我的職
業。自1989年以來,我看到了世界各地聽眾的昏昏欲睡的樣子、茫然不知的面容和困惑莫解的
表情 當我對一個較小的組進行室內訓練時,在練習過程中又發現了問題。即便那些面帶微笑
和會心點頭的學生,實際上對許多問題也還是糊塗的。通過近三年主持“軟體開發會議”的
C++分組討論會 我發現,我和其他講演者都有一種傾向,即過快地向聽眾灌輸了過多的主題
後來,我做了一些努力 通過區別對待不同層次的聽眾和調整表述內容的方法,儘量吸引聽眾
也許這是過分的要求 但是因為我是一個堅持傳統教學的人 所以希望通過努力 使每一個人
都能跟得上教學進度
有一段時間,我編寫了大量的教學簡報。這樣 我結束了通過實驗和重複方式進行學習
(在設計C++程式的過程中這也是很有用的一項技術)的階段。最後,從我多年的教學經驗中
總結出來的所有內容 形成了一門課程 在課程中,我用一系列離散的、易分解的步驟和舉辦
手把手的小型研討班的形式解決學習中的問題(理想的學習情況)並在每個短課後面跟隨著
練習
這本書是在兩年課程教學的基礎上寫成的 並且書中的內容在許多不同的研討班上通過了
多種形式的實際測試。我從每個研討班上收集反饋意見,不斷地修改和調整內容,直到我感覺
到它已經成為一本很好的教材為止。但這本書不僅僅是研討班的分發教材,而且我在其中放入
了儘可能多的信息,在結構上使得它能引導讀者順利地通過當前主題並進入下一個主題 另外
這本書也適合於自學讀者 能幫助他們儘快地掌握這門新的程式語言
3.目標
在這本書中 我的目標是:
1)以適當的進度介紹內容。每次將學習向前推進一小步 因此讀者能很容易地在繼續下一
步學習之前消化每個已學過的概念
2)儘可能使用簡短的例子。當然,這有時會妨礙我解決“現實世界’的問題。但是,我發
現,當初學者能夠掌握例子的每個細節,而不受問題的領域所影響時 他們通常會更有興趣。
另外,選材對代碼的長短也有嚴格的限制,以便代碼能在課堂上使用。為此 我無疑會受到使
用“玩具例子’的批評,但是我寧願承受這一批評,因為這樣更有利於教學 如果想要得到更
複雜的例子 可以查閱《C++Inside&Out》[2]的後面幾章。
3)仔細安排描述內容的順序,不讓讀者看到還沒有揭示的內容 當然 這不是總能做得到
的 在這種情況下,將會給出簡明的介紹性的描述
4)只把對於理解這門語言是重要的東西介紹給讀者,而不是介紹我知道的所有的內容。我
相信存在著“信息重要層次結構”有些內容是95%的程式設計師不需要知道的,這些東西只會
迷惑人們 增加他們對該語言複雜性的感覺,以致於C++現在被認為比ADA還複雜。舉一個C
語言的例子,如果我們記住運算符優先表(我是記不住的),就可以寫更漂亮的代碼。但是,
如果一定要這樣做 反而會使代碼的讀者或維護者糊塗。所以可以忘掉優先權,當不清楚時使
用括弧。對於在C++中的某些內容可以取同樣的態度,因為我認為這些內容對於寫編譯器的人
比對於程式設計師更重要
5)保持每一節的內容充分集中 使得授課時間以及兩個練習之間的間隔時間不長 這不僅
能使聽眾思想活躍,在研討班上精力集中,而且會使他們有更大的成就感。
6)幫助讀者打下堅實的基礎,使得他們能充分地理解這些問題,從而可以順利地轉向學習
更困難的課程和書籍。
4.章節安排
C++是一個在已經存在的文法上面增加了新的不同性能的語言(因此,它被認為是混合的
面向對象的語言)。由於很多人學習走了彎路,因此,我們已經開始探索C程式設計師轉移到C++語
言性能層上的方法。因為這是C語言訓練思想的自然延伸,所以我決定去理解和重複相同的道
路,並通過引出和回答一些問題來加速這一進程,這些問題是當我學習該語言時遇到的和聽眾
在聽我的課時提出來的。
設計這門課時,我始終注意一件事:人們學習C++語言的方法。聽眾的反饋意見幫助我認
識到哪些部分是很難學習的,需要額外解釋。在這個領域中,我曾經雄心勃勃,一次講解包括
了太多的內容。通過講解過程,我知道如果包括大量新性能,就必須對它們全部作出解釋,而
且學生也特別容易混淆。因此,我努力一次只介紹儘可能少的性能,理想的情況是每章一次只
介紹一個。
本書的目標是在每一章中只講授一個性能,或只講授一小組相關的性能,用這種方法,不
會有多餘的性能被關聯。這樣,在進入下一章的學習之前,學生可以對自己的當前知識融匯貫
通。為了實現這些目標,我離開C性能比我希望的還要遠。例如,我寧肯馬上用C++的輸入輸出
流庫,而不用熟悉的C語言printf()函式族,這樣就需要過早地介紹這一主題,因此,在較早的
章節中,允許C庫函式與它們並用。這樣做的好處是C程式設計師不會因為用到了許多未解釋的C++
性能而困惑,因此,對該語言的介紹將是平穩的,並且能反映出消化和理解這些性能的方法。
下面是對該書章節內容的簡要說明。
第1章 對象的演化。當項目對於維護變得太大和太複雜時,就產生了軟體危機。人們常
說,“我不能得到希望做的項目,即便能,它們也太昂貴”。這引出了一些問題,在本章中我將
討論這些問題,並且討論面向對象程式設計思想和如何運用這一思想解決軟體危機問題。另外,
在這一章中我還將闡述採用這種語言的好處,提出關於如何轉入C++世界的建議。
第2章 數據抽象。C++的許多性能都圍繞著一個根本的思想:創建新數據類型的能力。這
不僅可以提供超級代碼組織,而且為許多強大的OOP能力奠定了基礎。在本章中,讀者將可以
看到如何用將函式放入結構內部的簡單過程而實現這一思想,可以看到如何具體地完成這樣的
過程和創建什麼樣的代碼。
第3章 隱藏實現。通過說明結構中的一些數據和函式是private(私有的),可以把它們設
置為對於這個新結構類型的用戶是不可見的。這意味著能夠把下層實現和客戶程式設計師看到的接
口隔離開來,因此更容易改變具體實現,而不影響客戶代碼。另外,C++還使用關鍵字class作
為描述新數據類型的更奇特的方法。單詞“object”的意思並不神秘,在某種意義上它就是變
量。
第4章 初始化與清除。C語言的最通常的一類錯誤是由於變數未初始化而引起的。C++的
構造函式使得程式設計師能保證他的新數據類型的變數(即類的對象)總是能被恰當地初始化。如
果他的數據類型還要求某種方式的清除,他可以保證這個清除動作總是由C++的析構函式來完
成。
第5章 函式重載和預設參數。C++打算幫助程式設計師建立大而複雜的項目。這時,可能會引
進使用相同函式名的多個庫,還可能會在同一個庫中選擇具有不同含義的相同的名字。C++采
用函式重載使這一問題容易解決。重載允許當參數表不同時重用相同的函式名。預設參數使我
們能以不同的方法調用同一個函式,並能自動地對某些預設的參數提供預設值。
第6章 輸入輸出流介紹。輸入輸出流是提供基本I/O工具的C++庫。輸入輸出流希望以I/O
庫代替C庫的STDIO.H。I/O庫更容易使用,更靈活,且更可擴充――也就是可以讓它和新類一
起工作。對於標準I/O、檔案I/O、記憶體格式化,本章將教會讀者如何最好地使用已存在的輸入
輸出流庫。
第7章 常量。本章包含了const和vo1atile關鍵字,它們在C++中有另外的含義,特別是在
類的內部。本章還說明c0nst的含義在類的內部和外部有何不同,如何在類的內部創建編譯時
常量。
第8章 內聯函式。預處理宏省去了函式調用開支,但是也排除了有價值的C++類型檢查。
內聯函式具有預處理宏和實際函式調用的所有好處。
第9章 命名控制。在程式設計中,創建名字是基本的活動,而當項目變大時,名字的數
目是沒有限制的。C++允許在名字創建、可視性、存儲代換和連線方面有大量的控制。這一章
將說明如何用兩個技術控制名字。第一,static關鍵字用以控制可視性和連線,我們還將研究
它對於類的特殊含義。另一個在全局範圍內更有用的控制名字的技術是C++的namespace(名
字空間)性能,它允許把全局名字空間劃分為不同的區域。
第10章 引用和拷貝構造函式。C++指針的作用和C指針一樣,而且具有嚴格的C++類型檢
查的好處。繼Algo1和Pascal之後,C++使用了“引用”,這是處理地址的新方法,當我們使用
平常的符號時,引用讓編譯器處理地址操作。讀者還會遇到拷貝構造函式,它控制對象通過傳
值方式傳送給函式或從函式中返回。最後,本章還將解釋C++指向成員的指針(pointer-to-
member)。
第11章 運算符重載。這個性能有時被稱為“文法糖”。由於允許運算符也是函式調用,
這使得程式設計師使用他的類型在文法上更有趣。在這一章中,讀者將學到,運算符重載只是不同
類型的函式調用,學會如何寫自己的運算符重載,特別是當參數、返回類型混合使用和讓運算
符成為成員或友元時。
第12章 動態對象創建。一個空中交通系統將處理多少架飛機?一個CAD系統將需要多少
種形狀?在一般的程式設計問題中,我們不可能在程式運行之前知道所使用的對象的數量、生
命期和類型。在這一章中,我們將學習C++的new和delete,研究如何漂亮地通過在堆上安全地
創建對象而解決上述問題。
第13章 繼承和組合。數據抽象允許由草稿創建新的類型。通過組合和繼承,程式設計師可以
用已存在的類型創建新的類型;用組合方法,可以以老的類型作為零件組裝成新的類型;而用
繼承方法,可以創建已存在類型的一個更特殊的版本。在這一章中,讀者將學習這一文法,學
習如何重定義函式,理解構造和析構對於繼承和組合的重要性。
第14章 多態和虛函式。單靠讀者自己,可能要花九個月的時間才能發現和理解作為OOP
基礎的這一性能。而通過一些小而簡單的例子,讀者可以看到如何通過繼承創建一個類型族,
看到在這個類型族中如何通過公共基類對這個族中的對象進行操作。關鍵字virtual允許籠統地
談論這個族中的所有對象,這意味著大批代碼將不依賴於特殊類型的信息,因此,程式擴充性
好,構造程式和維護代碼也變得更容易和代價更低。
第15章 模板和包容器類。繼承和組合允許重用對象代碼,但不能解決有關重用的所有問
題。模板為編譯器提供了一種在類或函式體中代換類型名的方法,由此而重用原始碼。這就支
持了包容器類庫的使用,包容器類庫是使我們能快速而精力充沛地開發面向對象程式的重要工
具。這一章給出了這個基本主題的詳盡闡述。
第16章 多重繼承。乍聽起來這很簡單:一個新的類是從一個以上已存在的類繼承得來的。
然而,由此引起的基類對象的二義性和多拷貝問題會給我們造成困難。這些問題靠虛基類解決,
但是還有更大的問題:什麼時候要用到它?當需要通過用一個以上公共基類操作對象時,多重
繼承才是唯一必需的。這一章解釋多重繼承文法,並且介紹另外的方法,特別是,介紹如何用
模板解決一個共同的問題。用多重繼承修理一個“受損”的類界面是這一性能的主要的和非常
有價值的套用。
第17章 異常處理。在程式設計中,出錯處理總是很成問題。即便如實地返回了出錯信息
或設定了標記,函式調用者仍然容易忽視它。在C++中,異常處理是主要性能,當出現嚴重錯
誤時,它靠將一個對象從函式中“拋出”以解決這個問題。對於不同的錯誤,拋出不同類型的
對象。函式調用者在不同的出錯處理例程中“捕捉”這些對象,一旦拋出一個異常,就不能忽
略。所以能保證對錯誤做出必要的反應。
第18章 運行時類型識別。當我們僅給出一個指向基本類型的指針或引用時,運行時類型識
別(RTTI)可以找出對象的準確類型。通常,我們會有意忽略對象的準確數據類型,而讓虛函
數機制來實現這個類的正確行為。而有時候,知道只由基類指針指向的對象的準確類型是有用
的,這個信息常常允許更有效地完成特殊情況的運算。這一章解釋RTTI做什麼和如何使用它。
附錄A 其他性能。到寫這本書時,C++標準還沒有最終形成。雖然最終出現在這個語言
中的所有性能都會被加到這個標準中,但仍有一些還沒有出現在所有的編譯器中。這個附錄簡
要地說明在實際編譯器中(或在編譯器的未來版本中)會出現的其他性能。
附錄B 編程準則。這個附錄是對C++程式設計的一系列建議。這些建議主要是從我的講
課內容和程式設計練習中收集來的,還有的來自其他老師的見解,許多內容是這本書的總結。
附錄C 模擬虛構造函式。構造函式不能有任何虛性質,這是因為有時會因此產生笨拙的
代碼。這個附錄論述了關於“虛構造函式”的兩種方法。
5.練習
我已經發現,在研討班學習期間,簡單的練習對學生加強理解是特別有用的,因此,從第
2章起,每章後面都有一組練習。
這些練習難度適中,可以在一堂課內完成,老師可以通過觀察以確信所有的學生正在領會
這些內容。有些練習能激發優秀學生的學習興趣。它們被設計成能在短期內求解的規模,因為
這主要為了測試和複習學生的知識,而不是提出大的挑戰。
6.原始碼
這本書的原始碼是免費拷貝件,作為單個包分發的。讀者可以在許多公告板系統(例如
CompuServe和AOL)、Internet結點(例如SimTel檔案室;oak.oakland.edu是一個結點,參看
SimTel/msdos/Cplusplus)中找到它,並且在有些產品廠商的包中也包含它。這意味著讀者可以
免費共享這個包,可以傳送它到公告板和Internet上,也可以把它放在共享件包中。但是,不
能分塊分發這些代碼(即必須分發整個包)。代碼的著作權防止未經允許在印刷媒體上重新發表
這些代碼。讀者可以找到以ECKELT為檔案名稱開頭的檔案,在檔案名稱中的另外兩個字元用來表
示版本號,例如,ECKELT01.ZIP表示pkzip格式版本1(其他壓縮工具對其他系統適用)。在
每個原始碼檔案中會發現下面的著作權通告:
Copyright(c)bruceEckel,1995
Sourcecodefilefromthebook“ThinkinginC++”,
Prentice Ha1l, 1995, ISBN: 0-13-917709-4
AllrightsreservedEXCEPTasallowedbythefollowing
statements:Youmayfreelyusethisfileforyourown
work,includingmodificationsanddistributionin
executableformonly.Youmaycopyanddistributethis
file,aslongasitisonlydistributedinthecomplete
(compressed)packagewiththeotherfilesfromthis
bookandyoudonotremovethiscopyrightandnotice.
Youmaynotdistributemodifiedversionsofthesource
codeinthispackage.Thispackagemaybefreelyplaced
onbulletinboards,internetnodes,sharewaredisksand
productvendordisks.Youmaynotusethisfilein
printedmediawithouttheexpresspermissionofthe
author.BruceEckelmakesno
representationaboutthesuitabilityofthissoftware
foranypurpose.Itisprovided“asis”withoitexpress
orimpliedwarrantyofanykind.Theentireriskasto
thequalityandperformanceofthesoftwareiswith
you.Shouldthesoftwareprovedefective,youassume
thecostofallnecessaryservicing,repair,or
correction.
Ifyouthinkyou’vefoundanerror,please
emailallmodifiedfileswithloudlycommentedchanges
to:eckel@aol.com(pleaseusethesame
addressfornon-codeerrorsfoundinthebook).
讀者可以在他的項目和課堂上使用這些代碼,只要保留出現在每個源檔案中的著作權公告就
行。
編碼標準
在這本書的例子中,我使用特殊的編碼風格,這種風格已沿用了多年,並受到了Bjarne
Stroustrup的《C++ProgrammingLanguage》[1]的風格啟發。關於風格形式的話題可以爭論幾小
時,所以我並不試圖通過我的例子規定正確的風格。我有使用我規定風格的動機。因為C++是
自由形式的程式設計語言,所以我們可以使用任何已經習慣了的風格。
這本書中的程式是由字處理器自動地包含在正文中的檔案,是直接可編譯的。(在每個文
件的第一行中我用特殊的格式以便於這個包含,這一行以//:和跟隨檔案名稱開始。)這樣,在書
中印出的代碼檔案應當沒有編譯錯誤。那些會在編譯時引起錯誤信息的代碼用注釋//! 注出,
所以很容易被發現和用自動的方法被測試。作者已發現和得到報告的出現在被分發的代碼中的
錯誤已在這本書中訂正了。
這本書的指標之一是所有程式無編譯和連線錯誤(雖然有時引起警告)。一些程式只是示
范編碼例子,不表示獨立的程式,它們有空main()函式,如同
main(){}
這就使得連線器能無錯誤地完成連線。
在標準情況下main()返回int, 但是標準C++規定,如果在main()內沒有return語句,編譯器
將自動產生對應於return0的代碼。在本書中遵從這一規定(儘管一些編譯器對此仍可能產生
警告)。
7.語言標準
當認為與ANSI/ISOC標準一致時,全書將用術語:標準C。
雖然到寫這本書時ANSI/ISOC++委員會仍在為這個語言而工作,但這裡假設:被這個委
員會接受的性能都要設法實現,所有“主要的”性能都被增加進來。這樣,當提到寫這本書時
的在ANSI/ISO草案中的性能時,我使用術語:標準C++。記住,無論如何這個信息是基於我寫
這本書時的草案,而且,在這本書中描述的性能在標準完成之前有一些可能已經改變了。
8.語言支持
讀者的編譯器可能不支持在這本書中討論的所有性能,特別是當還沒有編譯器的最新版本
時。實現像C++這樣的語言是艱巨的任務,而且讀者可能希望將這些性能劃分成一些部分後分
別出現,而不是一下子全出現。如果讀者試用在這本書中的某個例子,從編譯器中得到了大量
的錯誤,這可能不是代碼或編譯錯誤,而可能是他的特定編譯器還沒有實現例子中的某個性
能。
9.錯誤
無論作者有多少發現錯誤的技巧,總有一些錯誤漏網,它們常常能被新讀者發現。如果讀
者發現了任何認為是錯誤的地方,請在最初的源檔案(讀者能在Internet上和其他來源中找到
它們,如本書前言“原始碼”部分所列)中明顯地注釋出錯誤,提出糾正建議,然後通過電子
郵件發往eckel@aol.com,以便在本書下一次印刷時更正。歡迎為下一個修訂本的補充練習或要
求提出建議。感謝讀者的幫助。
10.感謝
這本書中的思想和觀點有很多來源於:我的朋友,例如DanSaks、ScottMeyers、Charles
Petzold和MichaelWilk; 該語言的倡導者,例如BjarneStroustrup、AndrewKoening和R0b
Murray;C++標準委員會的成員,例如TomPlum、RegCharney、TomPenel10、ChuckAllison、
SamDruker、NathanMyers和UweStienmueller;在軟體開發會議上對我的C++教學發過言的人
們;經常參加我訓練班的學生,我認真地聽取他們問的問題,以使得這本教材更清晰。
我在為MillerFreeman公司出差旅途中向我的朋友RichardHaleShaw介紹了這本教材。
Richard(還有Kim)的見解和支持對我很有幫助。我還要感謝KoAnnvikoren、LisaMonson、
JulieShaw、NicoleFreeman、CindyBlair、BarbaraHanscome、YvonneLabat、ReginaRidley、
AlexDunne、JudyDeMocker和該公司的其他人員。
這本教材首先發表在由TylerSperry編輯的EmbeddedSystemsProgramming雜誌上。
C++編程思想 本書目錄
第一章 對象的演化第二章 數據抽象
第三章 隱藏實現
第四章 初始化與清除
第五章 函式重載與預設參數
第六章 輸入輸出流介紹
第七章 常量
第八章 內聯函式
第九章 命名控制
第十章 引用和拷貝構造函式
第十一章 運算符重載
第十二章 動態對象創建
第十三章 繼承和組合
第十四章 多態和虛函式
第十五章 模板和包容器類
第十六章 多重繼承
第十七章 異常處理
第十八章 運行時類型識別
附錄