模板與泛型編程
41.瞭解隱式接口和編譯器多態
classes和template都支持接口和多態
對classes而言接口是顯式的,以函數簽名爲中心。多態是通過virtual函數發生於運行期
對template參數而言,接口是隱式的,奠基於有效表達式。多態則是通過template具現化和函數重載解析發生於編譯器
這一條主要意思是,模板定義是編譯器的多態性,存在隱式接口。比如你一個模板函數裏面包含了模板參數的一些接口調用,這些接口本質上就是隱式接口
42.瞭解typename的雙重意義
聲明template參數時,前綴關鍵字class和typename可互換
請使用關鍵字typename標識嵌套從屬類型名稱;但不得在base class lists(基類列)或member initialization list(成員初值列)內以它作爲base class修飾符
- class和typename不等價的情況:修飾從屬類型名稱
- template內出現的名稱如果相依於某個template參數,稱之爲從屬名稱。如果從屬名稱在class內呈嵌套狀,我們稱它爲嵌套從屬名稱。如C::const_iterator(C是模板參數)
- C++解析從屬名稱,假設這個名稱不是一個類型,而是一個變量或其他東西。如果需要修改這個默認,需要自從屬名稱前放置typename,這是class不能替代的
43.學習處理模板化基類內的名稱
可在derived class template內通過“this->”指涉base class template內的成員名稱,或藉由一個明白寫出的“base class資格修飾符”完成
- 注意一個問題:但是用模板模式的時候,可能存在派生的模板類對模板參數類的函數的調用。此時,如果不是明確聲明,編譯器不會自動搜索模板參數類是否包含這個函數。而是在編譯時報錯(C++不進入templatized base classes 觀察)
- 當運用模板模式的時候,可能不是所有的派生類都完全符合模板類的所有接口,這個時候,需要全特化或偏特化模板
- 阻止C++不進入templatized base classes 觀察的行爲失效,有三種辦法
– 在base class函數調用動作前加上“this->”
– 使用using聲明
– 明白指出被調用函數位於base class 內(C::XXX())(這種方法不好,如果XXX是virtual函數,這樣會關閉virtual的綁定行爲)
44.將與參數無關的代碼抽離templates
Templates生成多個classes和多個函數,所以任何template代碼都不該與某個造成膨脹的template參數產生相依關係
因非類型模板參數而造成的代碼膨脹,往往可消除,做法是以函數參數或class成員變量替換template參數
因類型參數而造成的代碼膨脹,往往可降低,做法是讓帶有完全相同二進制表述的具現類型功效實現代碼
- 模板造成的膨脹:如果你的模板參數有300種可替代類型,將會有300個函數生成(class template的成員函數只有在被使用時才被暗中具現化,所以只有在這300個函數的每一個都被使用,纔會生成300個)
- 避免代碼膨脹,就是避免重複,歸納起來就是:共性與變性分析
- working set:對一個在“虛內存環境”下執行的繼承而言,其所使用的那一組內存頁
個人總結:模板會造成膨脹。非模板的參數會導致模板具現化的重複,通過實現模板基類,將非模板參數作爲某函數的形參,則繼承該基類的所有派生類共用同一份基類
45.運用成員函數模板接受所有兼容類型
請使用member function template生成“可接受所有兼容類型”的函數
如果你聲明member template 用於“泛化copy構造”或“泛化assignment操作”,你還是需要聲明正常的copy構造函數和copy assignment操作符
- 同一個template的不同具現體之間並不存在固有關係(這裏意思是如果以帶有繼承關係的B、D兩類型分別具現化某個template,產生出來的兩個具現體不帶繼承關係)
- 通過構造模板函數,減少代碼膨脹同時實現泛化的複製構造函數,注意,這裏的泛化複製構造函數,它本意是希望進行隱式轉化,所以不要使用explicit
- 在class內聲明泛化構造函數,不會阻止編譯器生成自己的copy構造函數