天天看點

《Effective C++》讀書筆記(四)七.模闆與泛型程式設計八.定制new和delete九.雜項讨論

七.模闆與泛型程式設計

模闆以前隻是學習的時候寫過一些例子,并沒有真正用過。本人對模闆還處于超級小白的狀态,基本看不懂這章,是以就先浏覽一遍吧,等今後有經驗之後,再回頭看一下模闆相關的内容。目前打算下一步學一下STL。

41.了解隐式接口和編譯期多态:

a)      classes和templates都支援接口和多态。

b)      對于classses中的接口是顯式的,以函數簽名為中心。多态通過virtual函數發生于運作期。

c)      Template中接口是隐式的,多态則通過template具現化和函數重載解析發生于編譯期。

42.了解typename的雙重意義:

a)      聲明template參數時,字首關鍵字class和typename等價。

b)      使用關鍵字typename辨別嵌套從屬類型名稱,但不得在基類列或者初始化清單中作為base class修飾符。

43.學習處理模闆化基類内的名稱:

44.将參數無關的代碼抽離templates:

45.運用成員函數模闆接受所有相容類型:

46.需要類型轉化時請為模闆定義非成員函數:

47.請使用traits classes表現類型資訊:

48.認識tempate元程式設計:

八.定制new和delete

New和Delete關鍵字是C++最常用的關鍵字之一,也是最重要的記憶體配置設定相關的東東,當然有必要很好學習一下。

49.了解new-handler的行為:

a)      傳統的new沒有異常處理,如果記憶體配置設定失敗,那麼就傳回null,而新的new在配置設定出錯的時候,是會抛出異常的,為了滿足一些老的程式,現在仍然有nothrow new,和老版本的new一樣,但是這樣做并不是很好,因為如果我們new一個對象,即使配置設定記憶體沒有出錯,但是構造函數一系列操作還是有可能有異常的。

b)      new-handler簡單來說就是異常處理的一個函數,我們可以通過std::set_new_handler(函數指針),來設定new的異常處理函數。

c)      WindowsGDI程式設計的時候,使用不同的畫筆或自體之前,先儲存起來原來的字型資源(預設的),然後使用自己定義的,使用之後再換回原本的。關于new-handler也可以這樣,先儲存起來原本的new-handler,然後使用自定義的new-handler,最後再換回原來的。這樣,我們就可以給不同的類定義不同的new-handler。

50.了解new和delete的替換時機:

這一條主要是講我們為什麼需要自己寫一個new和delete,而不是使用C++自帶的new和delete呢?主要有以下幾點:

a)      用來檢測使用時的錯誤,不管是什麼,自己寫的定制版總能增加一些額外的自己想要的功能,比如用來列印log。

b)      為了提升性能。C++自帶的關鍵字new,雖然能夠申請記憶體,但是這個new是需要照顧各種情況的,小記憶體,大記憶體,短周期,長周期都需要兼顧,是以對于單獨的某種記憶體配置設定,可能并不是最好的選擇。定制版的new可以加快配置設定速度,減少記憶體碎片等。

c)      定制版的new和delete可以收集程式記憶體使用的情況。

d)      保證資料類型齊位,有些機器比較苛刻,嚴格要求資料類型位對其,否則程式就會崩潰。有的即使不崩潰,也會降低運作效率。是以,保證資料齊位是很好的增加保險性與效率的方法。還有簇集的概念,當一組内容都位于一塊記憶體時,就可以減少記憶體調頁時缺頁的次數,這也是增加效率的方法。

e)      雖然自定義的new和delete這麼好,但是由于涉及到的内容還是很多,尤其是各種細節,是以,除非能力灰常強,還是不要自己寫了,更好的辦法是使用一些開源或者商業的庫。

51.編寫new和delete時要固守正常:

知道了為什麼要自己定制new和delete之後,下面就看一下自己定制這兩個操作時需要注意的地方。

a)      首先就是new操作符内部是一個死循環,申請記憶體成功時會跳出循環,如果失敗的話,會調用new-handler,最後會抛出bad_alloc異常。

b)      new内部對于申請0位元組記憶體采用的處理方式是改為申請1位元組。

c)      基類的如果被繼承了,那麼使用new的時候,申請的大小很可能不是基類的大小,而是子類的大小。這時候如果不做處理的話肯定會出問題。可以改為如果大小不等于基類,那麼直接改用::operator new(size)即原本的new進行處理。

d)      delete應該進行相關檢測,如果要釋放的指針為null,那麼就不進行處理。

e)      關于delete也應該注意釋放内容大于基類原本大小時的情況。也是可以調用::operater new(size)。但是,如果基類的析構函數不是virtual的話,可能傳遞給delete的size大小不正确,是以需要注意析構函數需要是virtual的。

52.寫了placement new也要寫placementdelete: 

a)      簡單的說,就是不管幹什麼,new和delete都是一對好基友,有new的地方就要有對應的delete,而且需要嚴格對應哦!我們自己寫一個new的時候,有可能加入placement,比如列印log等功能,這時候,就不能再使用原本的new了,需要找一個相應的delete和其對應。

b)      在我們覆寫了new和delete時要注意名字的覆寫原則,我們在類中自己定義了new和delete時,原本的new和delete就會被覆寫。我們需要清楚這一點。如果還想用正常的話,可以使定義的new傳回::new()。覆寫這個類的時候,在類中using 基類::operator new。

九.雜項讨論

53.不要輕易忽略編譯器的警告:

說來慚愧,能編過就OK,從不看warning的路過…

曾經聽過一個笑話,在懸崖上立着一個牌子寫着”warning”,大家走到那裡都很小心,但是一個程式猿同志卻跳下去了!雖然有點兒冷,不過事實确實如此,我們很大程度上忽略了編譯器苦口婆心的warning,而這些warning确實可能很有用!、

a)      《Effective C++》的作者舉了一個非const函數繼承的時候沒有覆寫基類const函數的例子,這個确實非常非常難以發現。不過我寫了一個例子,發現編譯器并沒有提醒我任何事,後來,我把警告等級調高之後,發現第一條就是提醒我沒有覆寫成功!看來,是時候看一看警告了,是時候将編譯器的警告級别調高了,争取獲得再高警告級别下也能寫出少警告的代碼。、

b)      不要過分依賴編譯器的報警能力,同樣的問題,有的編譯器會報警,而有的卻不會報警。不同的警告級别報告的内容也不同。是以,我們還是要自己提高代碼的嚴謹性。

c)      當我們真正對警告完全了解的時候,才可以忽視警告,千萬不能看都不看就忽視了!

54.讓自己熟悉包括TR1在内的标準程式庫:

C++學習到第二個階段,也是時候該進行下一步的學習了。首先,要了解一下C++到底都有什麼,文法熟悉了,常見的技巧也了解了,下面就是一些标準的程式庫了,站在巨人的肩膀上才能看得更遠!

a)      首先就是STL,這也是我下一步最想系統學習的一部分。本人是個算法渣渣,遇到資料結構就特别蛋疼,一直用的STL的一些基本的資料結構。感覺如果能系統的學習一下STL,今後在面對問題的時候就會有更多種選擇。

b)      還有一些比如iostream,wchar_t,異常處理等等。

c)      TR1并不是一個标準,而隻是一份文檔,真正的東西還是在STL中和Boost中。

55.讓自己熟悉boost:

之前就有聽說過很多關于boost的東東,比如不依賴作業系統的線程庫,shared_ptr等等。比如之後C++11标準之後就加入了多線程庫,可見,boost庫是C++标準庫的後備軍。

Boost中有好多好的東西,并且還在不斷擴充,C++真的是博大精深,沒有五年十年的時間,是不能掌握的。感覺未來的路很長,不過我喜歡!