C++沉思錄V2cn學習小結
————
部落格,http://blog.csdn.net/shunqiziranhao007/article/details/8693724
日期,2013年3月19日
————
2013年3月19日
————
這本書寫得真不錯,讓我接觸了好多電腦技術和思想。這本書我要多看幾遍,要好好體會裡面的思想。
————
序言
————
p1——那些書所關注的是語言本身,而不是如何應用這種語言。編程工作中最困難的部分並不是去學習語言細節,而是理解問題的解決之道。
一個程式員必須學習如何分析問題,這本書包含了大量的問題,以及針對這些問題的解決方案。認真地研習這些內容,將會有助於你成為更出色的程式員。
思想——抽象。
C++支援好幾種不同的抽象形式,如抽象資料類型,物件導向程式設計,泛型程式設計。
————
前言
————
p2——這是一本關于思想和技術的書,不是關於細節的。
我的意圖並不是教C++本身,而是想告訴你用C++編程怎樣進行思考,以及如何思考問題並用C++表述解決方案。知識可以通過系統學習擷取,智慧則不能。
p3——本書組織:介紹,繼承與物件導向,模板,類庫,特殊編程技術,回顧。
————
第0章 序幕—1—
————
p1——看看我是如何向他解釋什麼事情是C++可以做好而C做不好的。
0.1 第一次嘗試
p1——c++的核心概念就是類。
p3——使用者總是要求修改程式,後來證明這個問題在C++裡可以輕而易舉地解決,而在c裡卻得大動幹戈。
0.2 不用類來實現
0.3 為什麼用c++更簡單
p5——c傾向於不儲存狀態資訊,除非事先已經規劃妥當。c的話都是要自己考慮全面的而c++可以進行擴充。
0.4 一個更大的例子
0.5 結論
p7——C++提供了單個地方來描述所有這些東西,表明所有東西都是相互關聯的。通過把這有關係的事物聯絡起來,我們就能更加清晰地用C++來表達自己的意圖。
————
第一篇 動 機
————
p9——抽象是有選擇的忽略。
C++使我們更容易地把程式看作抽象的集合,同時也隱藏了那些使用者無須關心的抽象工作的細節,它設計時考慮了特殊使用者群的需求,它是為那些信奉實用主義的使用者群準備的。本書堅持兩個思想為核心:實用和抽象。
————
第1章 為什麼我用c++—11—
————
1.1 問題
p12——C++允許我定義資料結構的屬性,還允許我在用到這些資料結構時,把它們當作“黑匣子”使用。
1.2 曆史背景
1.3 自動軟體發布
p13——指標必須指到某個地方,應該什麼時候用什麼方法回收記憶體?
p14——寫這麼多調用真是很討厭,我肯定會漏掉一兩個的。
1.4 進入c++
p18——我採用了一個約定,即所有串以及類似串的值的大小都是動態決定的,相應的記憶體也是動態分配的。然而什麼時候釋放記憶體呢?這樣就會出現記憶體流失。
1.5 重複利用的軟體
p21——我認為C++有助於直接表達我的思想並實現我的目標。
1.6 後記
————
————
第2章 為什麼用c++工作—23—
————
2.1 小項目的成功
p23——很多最成功的、最有名的軟體最初是由少數人開發出來的。後面舉了很多例子。
軟體行業和其他很多行業不一樣,軟體製造的規模和經濟效益不成正比。
p25——我最鐘情的工具具有一個共性,那就是抽象的概念。當我在處理大問題的時候,這樣的工具總是能協助我將問題分解成獨立的子問題,並能確保它們相互獨立。也就是說,當我處理問題的某個部分的時候,完全不必擔心其他部分。
2.2 抽象
P27——C++在建構函式和解構函式中精確定義如何釋放動態分配的記憶體資源,雖然這種機制不是總像垃圾收集那樣靈活,但是在實踐中,它與許多應用更接近。另外,它有一個優勢是對環境要求低得多,記憶體一旦不用了就會被釋放,而不是等待垃圾收集機制發現之後才釋放。
2.3 機器應該為人服務
————
第3章 生活在現實世界中—29—
————
p31——車和程式設計語言都是工具,歸結起來就是解決問題的工具。
p32——明智的程式員對待Lisp以及任何其他程式設計語言的態度應當是:把它們當成工具,這一種合適時就採用這一種,如果另一種更管用,則選用另一種。
————
第二篇 類和繼承
————
p35——我一般不提OOP,但只要提到,我的意思就是指使用繼承和動態綁定的編程方式。
繼承是一種抽象,它允許程式員在某些時候忽略相似對象間的差異,又在其他時候利用這些差異。
只有在程式通過指向基類的指標或者基類的引用調用虛函數時,才會發生運行時的多態現象。
可以通過增加一個間接層來解決問題。傳統的C模型可能會建議採用指標來實現間接層。C++則採用控制代碼類。
————
第4章 類設計者的核查表—37—
————
你的類需要一個建構函式嗎?
你的資料成員是私人的嗎?
你的類需要一個無參的建構函式嗎?
是不是每個建構函式初始化所有的資料成員?
類需要解構函式嗎?
類需要一個虛解構函式嗎?
你的類需要複製建構函式嗎?
你的類需要一個賦值操作符嗎?
你的賦值操作符能正確地將對象賦給對象本身嗎?
你的類需要定義關係操作符嗎?
刪除數組時你記住了用delete[]嗎?
記得在複製建構函式和賦值操作符的參數類型中加上const了嗎?
如果函數有引用參數,它們應該是const引用嗎?
記得適當地聲明成員函數為const的了嗎?
p39——請記住,提這些問題倒不是希望你去尋求答案,只是希望能夠激勵你進行思考。這一章提出的那些問題都是很有用的,也很有意思,也激勵了我思考。
————
第5章 代理類—47—
————
5.1 問題
p48——我們要解決的問題是如何將不同的車放到一個停車場中,然後我們還能擷取這些車的一些相關資料。
5.2 經典解決方案
p48——提供一個間接層。
5.3 虛複製函數
5.4 定義代理類
p50——我們需要使用一個代理類來對屬於同一個繼承關係中類對象進行管理。
p52——3個技巧值得我們注意。
1.注意每次對copy的調用都是一個虛擬調用。
2.注意關於複製建構函式和賦值操作符中的v.vp非零的檢測。
3.注意對賦值操作符進行檢測,確保沒有將代理賦值給它自身。
5.5 小結
p53——代理類,這個類的每個對象都代表另一個對象,該對象可以是位於一個完整繼承層次中的任何類的對象。通過在容器中用代理對象而不是對象本身的方式,解決了我們的問題。
————
第6章 控制代碼:第一部分—55—
————
p55——控制代碼類允許在保持代理的多態行為的同時,還可以避免進行不必要的複製。
6.1 問題
p55——對於某些類來說,能夠避免複製其對象是很有好處的,有可能對象會很大,複製起來消耗太大。
通常由於參數和傳回值是通過複製自動傳遞的,所以C++函數可以很輕易地進行複製操作。
p56——由於這些類的對象通常被綁定到它們所控制的類的對象上,所以這些類常被稱為handle類,因為這些handle的行為類似指標,所以人們有時也叫它們智能指標。
6.2 一個簡單的類
p57——沒有指標成員,複製建構函式和賦值操作符使用預設的就好了。
6.3 綁定到控制代碼
p58——handle就是一種包含單個對象的容器。
6.4 擷取對象
6.5 簡單的實現
6.6 引用計數型控制代碼
p60——之所以使用控制代碼,原因之一就是為了避免不必要的對象複製。
upoint綁定point,handle是通過upoint來控制point的。
6.7 寫時複製
p62——注意考慮兩種情況,指標語義和值語義。要把這兩種情況的意思搞明白,不然第7章會看糊塗的。
p63——這一技術通常稱為寫時複製,其優點是只有在絕對必要時才進行複製(在第1次複製的時候沒有複製後面有情況了就需要複製了),從而避免了不必要的複製,而且額外開銷也只有一丁點兒。在涉及控制代碼的類庫中,這一技術經常用到。寫時複製實現值語義。
6.8 討論
————
第7章 控制代碼:第二部分—67—
————
7.1 回顧
7.2 分離引用計數
p70——對new和delete的使用要成雙成對出現,比較麻煩。下面是改進。都是遇到問題後,然後思考怎麼改進。那我們的問題是什嗎??我們要達到什麼樣的效果?
7.3 對引用計數的抽象
7.4 存取函數和寫時複製
7.5 討論
————
我看書看的有點稀裡糊塗,看得比較蛋疼,為什麼要舉這樣一個例子啊??我希望作者能夠先舉出一個錯誤的例子,然後讓我們自己去分析,錯在哪裡?怎樣解決這個問題?而不是說我這樣做更好,我這樣做有這樣那樣的效果。我表示看不明白。我不知道學習這個東西我能夠在哪裡得到應用?
——後面我重新看書了,把指標語義和值語義搞清楚了,就看明白了什麼意思了。
————
第8章 一個物件導向程式範例—75—
————
p75——通常認為,物件導向編程有3個要素,資料抽象、繼承以及動態綁定。這裡有一個程式,雖然很小,但是非常完整地展示了這3個要素。
8.1 問題描述
8.2 物件導向的解決方案
p76——繼承使得我們可以捕捉這些共同點,而動態綁定協助各個節點知曉它們的身份,這樣就不必讓這些對象的每個操作都必須時刻留心這個問題了。
p77——虛解構函式主要是用來保證在刪除由一個Expr_node*指標指向的對象時能夠調用到正確的衍生類別解構函式。多態。虛基類的存在只是為了讓繼承的子類獲得一些公用的介面而已。
8.3 控制代碼類
8.4 擴充1:新操作
8.5 擴充2:增加新的節點類型
p85——資料抽象和動態綁定使得在系統中增加新操作變得非常容易。
8.6 反思
————
很多筆記都寫在代碼那裡了,自己看吧。
————
第9章 一個課堂練習的分析(上)—89—
————
p89——我們兩人跑到斯坦福大學講授了為期一個星期的C++課程。類似這樣的課程,必須得有一個出色的課堂練習壓陣,才能取得良好的效果。想要學會遊泳,就得親自下水,在C++編程中學習C++,這是唯一可行之道。
9.1 問題描述
9.2 介面設計
9.3 補遺
9.4 測試介面
9.5 策略
9.6 方案
9.7 映像的組合
9.8 結論
————
邊看書邊看原始碼和寫注釋。
————
第10章 一個課堂練習的分析(下)—103—
————
10.1 策略
——在C++中,只要用到指標,就會涉及記憶體配置的問題。
10.2 體驗設計的靈活性
10.3 結論
——在解決問題的時候,有一點要始終牢記,不僅要看到眼前的問題,還要看到長遠的變化。
————
上一個方案,浪費空間且不夠靈活。
————
第11章 什麼時候不應當使用虛函數—121—
————
11.1 適用的情況
p121——只有當涉及到繼承時,才有必要考慮一些問題。
11.2 不適用的情況
p122——需要考慮的一些問題:
1.虛函數的代價並不是十分昂貴,但也不是免費午餐;在使用它們之前,要認真考慮其開銷,這一點十分重要。虛函數有時候會帶來很大的消耗。
2.有些情況下非虛函數能夠正確運行,而虛函數卻不行。虛函數並不總是提供所需的行為。
3.不是所有類都是為了繼承而設計的。有時我們寫一個類,可能不想考慮衍生類別問題。
11.3 解構函式很特殊
p127——記住虛函數和非虛函數之間的區別只有在這種特定的環境下才會體現出來:當使用一個基類指標或引用來指向或者引用一個衍生類別對象時。
11.4 小結
p129——小結好好看下。寫程式時我們必須考慮自己正在做什麼,僅僅根據規則和習慣思維行事是不夠的。
————
這一章表示沒怎麼看明白。以後重新看看。——後面看懂一些了。
————
第三篇 模板
————
這篇的東西我看得稀裡糊塗,可能是我積累不夠吧,目前還吸收不了,先看看其他的經典的書籍吧。——後來我學習了stl的原始碼,慢慢就能夠看懂這些以前看不懂的東西了。
p131——從某種意義上說,模板只不過是文法宏的一種受限形式。
為什麼我們認為模板如此重要呢?——答案是“實用性”。
————
第12章 設計容器類—131—
————
討論了設計容器類需要考慮的很多問題。
12.1 包含什麼
12.2 複製容器意味著什麼
p136——
結構體實現值語義:複製完成後,兩個變數都有這個值的獨立的副本。
數組實現引用語義:複製完成後,兩個變數都引用同一個底層對象。
後者的效率更高。
12.3 怎樣擷取容器的元素
12.4 怎樣區分讀和寫
12.5 怎樣處理容器的增長
p140——稍微成熟一點的分配策略是按區塊(chunk)增加容器的大小,這樣,新的記憶體空間必須一次分配完畢,這種策略的運行情況不錯,但是在選擇適當的計算新快大小的策略時,就需要格外謹慎了。(sgi的stl源碼中的 stl_alloc.h 的 __default_alloc_template類使用的就是這種記憶體配置策略 )。
12.6 容器支援哪些操作
12.7 怎樣設想容器元素的類型
12.8 容器和繼承
12.9 設計一個類似數組的類
p149——到目前為止,我們知道了如下的事情:
1.對內建數組使用指標比使用下標要方便得多,因為使用下標要知道使用哪個數組。
2.但是在類似數組的類中使用指標會帶來一個缺陷:改變這樣的類的實現或者增加它的操作都會突然使指向它的元素的指標失效。
————
第13章 訪問容器中的元素—151—
————
13.1 類比指標
p151——C++最基本的設計原則是用類來表示概念。
p152——考慮建構函式、解構函式、複製和賦值是設計類的一個很好的起點。
13.2 擷取資料
13.3 遺留問題
p156——解決這類問題的方法就是運用我經常稱為“軟體工程基本定理”的思想:通過引進一個額外的中介層,我們能夠解決任何問題。
13.4 指向const array的pointer
13.5 有用的增強操作
————
第14章 迭代器—167—
————
14.1 完成 pointer類
14.2 什麼是選代器
14.3 刪除元素
14.4 刪除容器
14.5 其他設計考慮
14.6 討論
————
第15章 序列—175—
————
15.1 技術狀況
——庫中必須包括使用者想要的所有功能。
15.2 基本的傳統觀點
15.3 增加一些額外操作
15.4 使用範例
15.5 再增加一些
15.6 請你思考
————
邊看書邊看代碼。
————
第16章 作為介面的模板—191—
————
16.1 問題
16.2 第一個例子
16.3 分離迭代方式
16.4 遍曆任意類型
16.5 增加其他類型
16.6 將儲存技術抽象化
16.7 實證
16.8 小結
————
類比迭代器的操作。
————
第17章 模板和泛型演算法—203—
————
p204——提到STL至少是Alex開發的第四個類似的庫,並且是第二個用C++寫的庫(他以前採用的是Ada和Schema)。
17.l 一個特例
p204——我們將通過儘可能的去除假設條件來提高這個演算法的通用性。
17.2 泛型化元素類型
17.3 延遲計數
17.4 地址獨立性
17.5 尋找非數組
17.6 討論
p211——Alex和他的同事們對他們的庫做了大量的工作,就是來處理這些細節問題的。
————
第18章 泛型迭代器—213—
————
18.1 一個不同的演算法
18.2 需求的分類
p216——這些討論說明必須對需求進行適當的分類——我們必須找出一套能很好適合某個演算法集的需求,以及另一套適合另一個演算法集的需求,以此類推。
18.3 輸入選代器
18.4 輸出迭代器
18. 5 前向選代器
18.6 雙向迭代器
18.7 隨機存取送代器
18.8 是繼承嗎
p220——概念繼承。
18.9 效能
18.10 小結
————
第19章 使用泛型迭代器—223—
————
19.1 迭代器類型
19.2 虛擬序列
19.3 輸出資料流迭代器
p228——我們可以使用ostream_iterator類本身來使*out 和 out 等價。
19.4 輸入資料流迭代器
19.5 討論
————
看stl源碼去。
————
第20章 迭代器配接器—233—
————
20.1 一個例子
20.2 方向不對稱性
p235——只是因為C和C++語言保證(且只保證)能夠定位超出了任何數組尾部之後的那一個元素的地址。換句話說,就是a+10是合法的,而a+11是非法的,a-1也是非法的。
20.3 一致性和不對稱性
20.4 自動反向
p238——唯一要記住的就是解除引用操作實際發生在私人資料成員所指向元素的前一個元素。
20.5 討論
p240——只要小心使用,迭代器配接器可以極大地減少我們要寫的演算法數量,或者達到事半功倍的效果。
————
第21章 函數對象—241—
————
p241——由於函數對象十分靈活,能使很複雜的事情成為可能,所以它們首先是難於理解的。
21.1 一個例子
21.2 函數指標
21.3 函數對象
21.4 函數對象模板
21.5 隱藏中間類型
21.6 一種類型包羅永珍
21.7 實現
21.8 討論
p253——研究這個例子的意義之一,讓大家體會到我們必須做大量的工作才能繞過一個看似簡單的語言局限。另一點就是這個例子說明了擴充語言以使之允許函數組合,絕不像看上去的那麼簡單。
————
第22章 函數配接器—255—
————
22.1 為什麼是函數對象
22.2 用於內建操作符的函數對象
22.3 綁定者(binders)
22.4 更深入地探討
22.5 介面繼承
22.6 使用這些類
22.7 討論
p261——理解的難易程度總是和熟悉程度密切相關。
————
第四篇庫
————
p263——如果某一組類的設計和實現是希望得到廣泛應用,那麼我們就稱之為庫。
所謂的“聰明”,還要求類介面的設計有助於減少使用者可能犯下的錯誤,這一點總是被人忽視。
————
第23章 日常使用的庫—265—
————
23.1 問題
23.2 理解問題:第1部分
23.3 實現:第1部分
23.4 理解問題:第2部分
23.5 實現:第2部分
23.6 討論
————
講了如何使用庫來解決實際問題。
————
第24章 一個庫介面設計執行個體—275—
————
24.1 複雜問題
24.2 最佳化介面
24.3 溫故知新
24.4 編寫代碼
24.5 結論
————
使用C++來實現C的功能。抽象。。
————
第25章 庫設計就是語言設計—283—
————
25.1 字串
25.2 記憶體耗盡
p284——要考慮到記憶體配置失敗的問題。作者好像在講為什麼需要異常處理?作者很為使用者著想!!考慮到了使用者的各種情況。我來講講為什麼需要異常處理,使用異常處理機制比較方便使用,try catch可以自動貼齊到拋出的異常,然後我們可以進行相關處理。如果不用異常處理,則需要我們自己每次都進行檢查,然後根據檢查的結果進行相關處理,這樣就變得很麻煩了,是一種負擔啊。如果在類函數內部進行檢查則把處理方法給寫死了,不方便。
25.3 複製
p288——複製建構函式和賦值操作符之間的主要區別在於,賦值操作符複製新值進來前必須刪除舊值。可以把複製部分可以分離出來寫成一個函數。
25.4 隱藏實現
25.5 預設建構函式
25.6 其他動作
25.7 子字串
25.8 結論
p296——即使簡單的通用類也引發大量的設計問題。
————
第26章 語言設計就是庫設計—297—
————
——設計一個好程式庫的要求之一就是徹底隔離介面和實現。
26.1 抽象資料類型
26.1.1 建構函式與解構函式
26.1.2 成員函數及可見度控制
26.2 庫和抽象資料類型
26.2.1 型別安全的連結(linkage)
26.2.2 命名空間
26.3 記憶體配置
26.4 按成員賦值和初始化
26.5 異常處理
26.6 小結
————
第五篇技術
————
————
第27章 自己跟蹤自己的類—309—
————
27.1 設計一個跟蹤類
27.2 建立無作用程式碼
27.3 產生對象的審計跟蹤
27.4 驗證容器行為
27.5 小結
————
第28章 在簇中指派至—321—
————
28.1 問題
28.2 設計方案
28.3 實現
28.4 加入繼承
28.5 小結
————
??
————
第29章 應用器、操縱器和函數對象—329—
————
29.1 問題
29.2 一種解決方案
29.3 另一種不同的解決方案
29.4 多個參數
29.5 一個例子
29.6 簡化
——一個產生函數對象,另一個建立operator<<應用器。
29.7 思考
29.8 記錄、參考資料和致謝
————
蠻有意思的。
————
第30章 將應用程式庫從輸入輸出中分離出來—341—
————
——設計一個類來表示任意IO庫的介面,從而使應用程式與IO之間的耦合度大為降低。
30.1 問題
30.2 解決方案1:技巧加蠻力
30.3 解決方案2:抽象輸出
30.4 解決方案3:技巧,但無蠻力
30.5 評論
——本章展示了模板可以怎樣提供一種對操作進行抽象化的方法,就像類對資料結構提供抽象化一樣。
————
第六篇總結
————
p349——由於要用c++來解決很多複雜的問題,所以導致它很複雜。為了提供強大的表現力和高效率,c++變得複雜。
————
第31章 通過複雜性擷取簡單性—351—
————
31.1 世界是複雜的
31.2 複雜性變得隱蔽
p352——人們希望得到更好的效能和更方便的使用,汽車就變得更加複雜。而且那些複雜性隱藏得更深了。
31.3 電腦也是一樣
p353——產生這種情況的原因在於複雜度轉移到了那些不需要單獨製作或配置的組件中了。
p354——換言之,我們知道怎樣用工藝技術取代複雜度了。
31.4 電腦解決實際問題
31.5 類庫和語言語義
p355——在FORTRAN中建立這樣的結構是很難的;而在c++中則未必很難。c++中的抽象機制使得在別的語言中很困難甚至根本不可能實現的工作變得簡單容易。
p356——賦值與初始化的區別,String s = "hello"; 和 String s; s = "hello"; 當我們在第二個例子中給s賦值時,s早就有一個值了。我們必須在處理新值之前釋放舊值佔用的記憶體。
31.6 很難使事情變得容易
p357——經過精心設計的c++庫非常好用。庫的使用者運用這些庫感覺就像使用某種恰好適合他們自己的問題的特殊語言一樣。
31.7 抽象和介面
31.8 複雜度的守恒
p359——抽象仍將是我們處理這個複雜世界的最有力的工具。
————
第32章 說了hello world後再做什麼—361—
————
32.1 找當地的專家
p361——由於C++源自C,所以我們還是有必要好好學習C語言的。向前輩學習!!記住,任何有用的程式都必須和外部世界交流。
32.2 選一種工具包並適應它
32.3 c的某些部分是必需的
——使C的某些思想成為習慣。要徹底理解各種複雜常用的聲明。
p363——這頁的例子蠻有意思的,幸好我能夠看懂。
32.4 c的其他部分不是必需的
p364——c++比c具有更強的類型檢查能力。
p365——c++中複製對象並不總等價於按位複製組成這個對象的位元組。相反,複製建構函式和賦值操作符決定要怎樣複製這個類的對象。其實這裡提到的就是,複製的時候,是淺複製還是深複製的問題,主要是類中有指標成員,淺複製,它只複製到了地址,沒有把地址所指向的內容複寫到,當原先那個對象被銷毀後,那段地址中的內容也沒有了,就出錯了。而深度複製則會重新分配一定大小的記憶體來儲存內容,這樣就不存在前面的問題了。
32.5 給自己設一些問題
p366——如果你只依賴所完全理解的那部分知識,就必須想辦法擴充你的理解範圍。增加知識儲備的最有效辦法就是用已知的方法嘗試新問題。選擇一個你還不理解的C++特性,使用這個特性寫個程式,但是注意除此之外,只使用你已掌握的東西。然後,做一些其他的工作,協助你確信程式在做什麼——以及為什麼。
p367——這部分的內容還是很值得學習的。通過構造各種小巧的類在許多領域都是加強理解的好辦法。
32.6 結論
p368——做理解的事情,理解要做的事情。逐步加深擴充理解。從c到c++最大的觀念性就是要停止考慮程式的結構,而開始考慮程式資料的行為。早些考慮測試。多思考。你都要清楚為什麼這樣做。
————
附錄 koenig和moo夫婦訪談—371—
————
p373——學習C++應該首先掌握如何使用程式庫,這樣更容易理解類的概念,學會如何構造類的技術。
p374——所謂物件導向編程,就是使用繼承和動態綁定機制編程。