第六章 繼承與面向物件設計
32. 確定你的public繼承塑模出is-a關系
public繼承意味著要塑模出is-a的關系,所以每一個子類物件也是相應的父類物件,故適用于父類物件的每一個操作也都適用于子類物件,
33. 避免遮掩繼承而來的名稱
派生類中的名稱(包括資料名稱和函式名稱(不考慮不同傳參))會遮掩基類中的名稱,這種機制類似于作用域,先從當前類中搜索名稱,如果找不到則到基類中搜索,以此類推,這個名稱遮掩規則主要是防止新建的子類繼承了較為疏遠的基類同名函式,這在大部分情況下是不期望的,如果期望不要遮掩,使用using顯式暴露即可,
34. 區分介面繼承和實作繼承
class的成員函式可分為三類:一般函式、虛函式、純虛函式,一般函式的介面和實作都會被強制繼承;虛函式主要是繼承介面且繼承一個預設實作;純虛函式只有介面繼承,帶有virtual關鍵字的虛函式和純虛函式可通過虛函式表訪問到實際類物件的成員函式,即多型呼叫,
35. 考慮virtual函式以外的其他選擇
① virtual函式的替代方案包括NVI(non-virtual interface)和strategy設計模式的多種形式,NVI本身也是一種特殊形式的Template method設計模式;
② 將機能從成員函式移動到class外部的缺點是:非成員函式無法訪問class的non-public成員;
③ std::function物件即泛化的函式指標,可指代一切可呼叫物件,只要可呼叫物件的函式簽名一致,常與std::bind配合使用,用以實作回呼機制,
36. 絕不重新定義繼承而來的non-virtual函式
這條準則在多型呼叫的類繼承體系中是很重要的,因為一般情況下,多型呼叫我們會將一個子類物件的地址賦給一個父類指標,然后通過呼叫父類中的介面,去執行子類中的對應實作,實作這個目的的前提是呼叫的函式是虛函式(包括純虛函式),這樣才能從虛函式表中找到真正的函式地址,然而如果呼叫的是非虛函式,那么呼叫的函式將是指標靜態型別所對應的靜態系結的成員函式,這不是我們期望的,當然,如果僅僅是使用子類而不多型呼叫,重新定義父類非虛函式也未嘗不可,那么父類中的同名函式將被遮掩,
37. 絕不重新定義繼承而來的預設引數值
既然是重新定義,那么該成員函式必將是virtual的,virtual函式是動態系結的,但預設引數值是靜態系結的,所以如果你將繼承而來的virtual函式修改了預設引數值,那是不會生效的,多型呼叫時將傳入基類指定的預設引數,
38. 通過復合塑模出has-a關系或“根據某物實作出”
復合即一個物件擁有另一個物件,即塑模了has-a的關系;
在應用域,復合意味著A has-a B,即具體某個東西A擁有著另一個東西B;而在實作域,復合意味著根據某物B實作出某物A,即某物A的實作是要靠B的某些特性來完成的,如根據queue實作出stack,
39. 明智而審慎地使用private繼承
private繼承意味著根據某物實作出,它要比復合的級別低,當你想要創建一個class A,需要用到另一個class B的某些特性時,直接復合是很直觀的手段,但是,如果你想對B的某些介面進行適配改造的話,使用private是一個明智的選擇,
40. 明智而審謹地使用多重繼承
多重繼承要比單一繼承復雜,有可能導致歧義,一個常用的場景是:子類public繼承某個介面類,并private繼承某個輔助類,兩相結合來使用,
第七章 模板與泛型編程
模板元編程是C++一個非常大且難掌握的模塊了,這塊內容還掌握的很淺,現在只會將引數型別抽到模板函式實作上,相關實踐較少,等有點積累了再深入學習,
41. 了解隱式介面與編譯期多型
① class合template都支持介面和多型
② class的介面是顯式的,以函式簽名為中心,多型是通過virtual函式發生于運行期;
③ template的介面是隱式的,奠基于有效運算式,其多型是通過template具現化和函式多載決議發生于編譯期,
42. 了解typename的雙重意義
① 宣告template引數時,前綴關鍵字class和typename等價
② 請使用關鍵字typename標識嵌套從屬型別名稱;但不得在base class lists(基類列)或member initialization list(成員初始列)內作為base class修飾符,
43. 學習處理模板化基類內的名稱
可在派生類內通過"this->"指涉base class template內的成員名稱,或籍由一個明白寫出的"base class 資格修飾符"完成,
44. 將與引數無關的代碼抽離template
① Template生成多個classes和多個函式,所以任何template代碼都不該與某個造成膨脹的template產生依賴關系,
② 因非型別模板引數而造成的代碼膨脹,往往可以消除,做法是以函式引數或class成員變數替代template引數,
③ 因型別引數而造成的代碼膨脹,往往可以降低,做法是讓帶有完全相同二進制表述的具現型別共享實作碼,
45. 運用成員函式模板接受所有兼容型別
① 請使用成員函式模板生成“可接受所有可兼容型別”的函式,
② 如果你宣告成員函式模板用以“泛化copy構造”或“泛化copy賦值符”,你還需要宣告正常的copy構造函和copy賦值運算子,
46. 需要型別轉換時請為模板定義非成員函式
略
47. 請使用traits classes表現型別資訊
略
48. 認識template元編程
① 模板元編程可將作業由運行期移往編譯期,從而可以實作早期的錯誤診斷和更高的執行效率;
② 模板元編程可被用來生成“基于政策選擇組合”的客戶定制代碼,也可用來避免生成對某些特殊型別并不合適的代碼,
第八章 定制new和delete
這章的內容深入理解的話,講的就是C++記憶體模型的相關東西了,這部分內容都可以單獨開一門課了,這里只做簡單記錄,
49. 了解new-handler的行為
① set_new_handlers允許客戶指定一個函式,在記憶體分配無法獲得滿足的時候被呼叫,
② Nothrow new是一個頗為局限的工具,因為它只適用于記憶體分配;后繼的建構式呼叫還是有可能拋出例外,
50. 了解new和delete的合理替換時機
有許多時候需要寫個自定義的new和delete,包括改善性能、對heap運用錯誤進行除錯、收集heap使用資訊,
51. 撰寫new和delete時需要固守常規
① operator new應該內含一個無窮回圈,并在其中嘗試分配記憶體,如果它無法滿足需求,就該呼叫new-handler,它應該有能力處理0 bytes申請,class專屬版本則還應該處理“比正確大小更大的(錯誤)申請”;
② operator delete應該在收到null指標時不做任何事情,class專屬版本則還應該處理“比正確大小更大的(錯誤)申請”,
52. 寫了placement new時也要寫placement delete
① 當你寫了一個placement operator new,請確定也寫出了對應的placement operator delete,如果沒有這樣做,你的程式可能會發生隱蔽而時斷時續的記憶體泄漏,
② 當你宣告placement new和placement delete時,請確認不要無意識地遮掩了它們地正常版本,
第九章 雜項討論
53. 不要輕易忽視編譯器的警告
① 嚴肅對待編譯器發出地警告資訊,努力在你的編譯器地最高(最苛求)警告級別下爭取“無任何警告”的榮譽,
② 不要過度依賴編譯器的報警能力,因為不同編譯器對待事情的態度并不相同,一旦移植到另一個編譯器上,你原本依賴的警告資訊可能消失,
總結:編譯時的警告資訊通常被我們忽略,而有些莫名其妙的錯誤通常在編譯時就已經以警告的方式呈現出來了,所以盡可能正確無警告,據說華為的代碼要求編譯無警告才能上線,不知真偽,但這也不失為一種保證運行時穩定的策略,
54. 讓自己熟悉包括TR1在內的標準庫程式
① C++標準程式庫主要機能由STL, iostream、locales組成,并包含C99標準庫程式;
② TR1添加了智能指標、泛化函式指標(function<>)、hash-based容器、正則運算式以及另外10個組件的支持;
③ TR1自身只是一份規范,為獲得TR1提供的好處,你需要一份實物,一個好的實物來源是Boost,
55. 讓自己熟悉Boost
Boost提供了許多TR1組件實作品,以及其他許多程式庫,Boost可看作是一個對STL的擴展,也是很多新特性的試驗場,諸如function、bind、智能指標這些特性首先出現在了這里,經過很多用戶測驗證明其穩定性后,也在后續的C++版本中并入了STL.
小結
《Effective C++》真的是一本C++程式員必讀的書籍,雖然我現在通讀了一遍,但對其中的有些準則還是理解不夠透徹,主要原因還是實踐太少,后續編碼的時候要奪取刻意的思考一下,如何才能寫出易維護且高效正確的C++代碼,這本書的前6章節堆起來比較透徹容易理解,但從第7章開始談起模板元編程和記憶體時,確實是有點晦澀難懂了,原因還是平時寫代碼模板接觸較少、記憶體也是調介面直接申請,這兩部分后續有時間還需單獨深入學習,在我寫這篇總結的時候,其中有些條款的說法可能已經過時了,比如對某些TR1中的函式的呼叫,其實早已融入到STL中了,但其中的思考方式、設計原因等才是能讓我們觸類旁通、拓展到解決其他類似問題的核心知識,侯捷老師的書和課總是給人一種“授人以魚不如授人以漁”的感覺,受益匪淺,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/486246.html
標籤:C++
