C++關於程式碼重用的那些事
C++的另一個目標是促進程式碼重用。公有繼承是實現這種目標的機制之一,但並不是唯一的機制。還可以使用這樣的類成員:本身是另一個類的物件,這種方法稱為包含、組合或層次化。另一種方法是使用私有繼承或保護繼承。類模板是另一種重用程式碼的方法,類模板使我們能夠使用通用術語定義類,然後使用模板來建立針對特定型別定義的特殊類。
包含物件成員的類
通過組合(包含)即建立一個包含其他類物件的類,用於建立has-a關係。使用公有繼承時,類可以繼承介面,可能還有實現(基類的純虛擬函式提供介面,但不提供實現),獲得介面是is-a關係的組成部分。而使用組合,類可以獲得實現,但不能獲得介面,不繼承介面是has-a關係的組成部分。
C++包含讓程式設計師能夠限制程式結構的特性——使用explicit防止單引數建構函式的隱式轉換,使用const限制方法修改資料等等。
當初始化列表包含多個專案時,這些專案被初始化的順序為它們被宣告的順序,而不是它們在初始化列表中的順序。
被包含類物件的介面不是公有的,但可以在類方法中使用它。
私有繼承
使用私有繼承,基類的公有成員和保護成員都將稱為派生類的私有成員,這意味著基類方法將不會稱為派生物件公有介面的一部分,但可以在派生類的成員函式中使用它們,基類的公有方法稱為派生類的私有方法,派生類不繼承基類的介面。使用私有繼承,類將繼承實現。
要使用私有繼承,需要使用關鍵字private而不是public來定義類(private是預設值,省略訪問限定符也將導致私有繼承)。
使用私有繼承時,只能在派生類的方法中使用基類的方法,使用包含時將使用物件名來呼叫方法,而使用私有繼承時將使用類名和作用域解析運算子來呼叫方法。
多重繼承
使用多個基類的繼承被稱為多重繼承(MI)。C++多重繼承中引進了一種新技術——虛基類。虛基類使得從多個類派生出的物件只繼承一個基類物件。如果類有間接虛基類,則除非只需使用該虛基類的預設建構函式,否則必須顯式的呼叫該虛基類的某個建構函式。
多重繼承可能導致函式呼叫的二義性,可以使用作用域解析運算子來確定使用的方法,更好的辦法是重新定義該函式。還有一種辦法是使用模組化方式。
如果類從不同的類繼承了兩個或更多的同名成員(資料或方法),則使用該成員名時,如果沒有用類名進行限定,將導致二義性,如果某個名稱優先於其他所有名稱,則使用它時,即便不使用限定符,也不會導致二義性,派生類中的名稱優先於直接或間接祖先類中的相同名稱。
類模板
繼承和包含並不總是能滿足重用程式碼的需要,C++的類模板為生成通用的類宣告提供了一種更好的方法,模板提供引數化型別,即能夠將型別名作為引數傳遞給接收方來建立類或函式。
類模板的定義
模板類以下面這樣的程式碼開頭:
template <class Type>
關鍵字template告訴編譯器,將要定義一個模板,尖括號中的內容相當於函式的引數列表,可以把關鍵字class看做是變數的型別名,該變數接受型別作為其值。class並不意味著Type必須是一個類,而只是表明Type是一個通用的型別說明符,在使用模板時,將使用實際的型別替換它,較新的C++實現允許使用關鍵字typename代替class。
模板不是類和成員函式定義,它們是C++編譯器指令,說明如何生成類和成員函式定義,模板的具體實現被稱為例項化或具體化。由於模板不是函式,它們不能單獨編譯,模板必須與特定的模板例項化請求一起使用。
類模板的使用
僅在程式中包含模板並不能生成模板類,而必須請求例項化,為此,需要宣告一個型別為模板類的物件,方法是使用所需的具體型別替換泛型名。
指定特殊的型別而不是用作泛型名稱為非型別或表示式引數,表示式引數可以使整形、列舉、引用或指標,模板程式碼不能修改引數的值,也不能使用引數的地址,例項化模板時,用作表示式引數的值必須是常量表達式。
模板多功能性
可以將用於常規類的技術用於模板類,模板類可用作基類,也可用作元件類,還可用作其他模板的型別引數,模板的多功能性有:
- 遞迴使用模板,在模板語法中,維的順序與等價的二維陣列相反。
- 模板可以包含多個型別引數。
- 可以為模板型別引數提供預設值。
模板具體化
類模板有隱式例項化、顯式例項化和顯式具體化,它們統稱為具體化。模板以泛型的方式描述類,具體化是使用具體的型別生成類宣告。
- 隱式例項化
一般使用的都是隱式例項化:宣告一個或多個物件,指出所需的型別,而編譯器使用通用模板提供的處方生成具體的類定義。
- 顯式例項化
當使用關鍵字template並指出所需型別來宣告類時,編譯器將生成類宣告的顯式例項化,宣告必須位於模板定義所在的名稱空間中。
- 顯式具體化
顯式具體化是特定型別(用於替換模板中的泛型)的定義。