虚拟函式的呼叫时机
第 13 章 多重繼承與 虛擬函式 本章提要 13-1 多重繼承 13-2 虛擬基礎類別 13-3 虛擬函式 13-4 純虛擬函式與抽象類別 13-5 綜合演練 13-1 多重繼承 多重繼承意指在繼承時, 指定了多個父類別 (基礎類別);而虛擬函式則是在繼承結構下, 提供一種更直覺的物件操作方式, 讓開發人員能以更物件導向的方式撰寫程式。 同時繼承多個父類別 多重繼承下的物件建構 存取同名的成員 同一個基礎類別的多重繼承 同時繼承多個父類別 一個子類別可以繼承自一或多個父類別, 如果有多個父類別的話, 那麼就必須用逗號將之分開, 而且每個父類別之前均可個別指明其繼承方式。這就是所謂的多重繼承 (Multiple inheritance)。舉例來說, 自行車是一種交通工具, 同時也是一種運動器材: 同時繼承多個父類別 如此一來 Bicycle 類別將同時繼承 Vehicle 及 ExerciseTool 類別的成員。 在多重繼承時, 每個父類別只能出現一次, 而且必須是已定義好的類別, 否則會造成編譯時期的錯誤。雖然 C++ 並未限制父類別的數量, 但一般都只會用到兩個父類別, 以免形成太複雜的類別階層關係。 多重繼承下的物件建構 使用多重繼承時和單一繼承關係時相同, 在程式中宣告衍生類別物件時, 編譯器會自動呼叫父類別的建構函式然後才呼叫子類別的建構函式。只不過有多個父類別時, 這些父類別建構函式的呼叫順序, 是依照子類別定義中的次序來呼叫, 請參見以下的範例: 多重繼承下的物件建構 多重繼承下的物件建構 多重繼承下的物件建構 多重繼承下的物件建構 由於第 26 行定義 Bicycle 類別時, 是先寫 Vehicle, 再寫 ExerciseTool, 所以建構 Bicycle 的物件時, 編譯器會先呼叫 Vehicle 的建構函式再呼叫 ExerciseTool 的建構函式, 如執行結果所示。 多重繼承下的物件建構 多重繼承下的衍生類別, 其用法和單一繼承時大同小異, 例如同樣可存取父類別中的 public、protected 成員;編譯器在必要時會自動處理轉型等。但如果不巧不同父類別中有同名的資料成員或成員函式時, 我們要如何存取想要使用的成員呢? 存取同名的成員 當多重繼承下的父類別中有同名的資料成員或成員函式時, 若不指定成員所屬的父類別名稱, 則會出現語意不明 (ambiguity) 的情況, 也就是編譯器無法判斷程式要使用的是哪一個成員, 因此編譯時會出現錯誤。例如: 存取同名的成員 存取同名的成員 存取同名的成員 要特別注意的是, 這種語意不明的錯誤和函式多載並無關係。我們知道多載函式可藉著不同的簽名 (參數數量與型別) 來區分, 在上述這種同名函式並非多載函式, 因為它們原本就分屬不同的類別, 因此即使兩個父類別的同名函式有不同的簽名, 仍是會引發語意不明的編譯錯誤。換句話說, 就算函式簽名不同, 呼叫父類別的同名函式時, 一定要明確用範圍解析算符指出呼叫的函式所屬類別, 請參考以下的範例: 存取同名的成員 存取同名的成員 存取同名的成員 存取同名的成員 在 Vehicle 及 ExerciseTool 類別中, 我們故意將同名的 price 資料成員分別宣告為 double 及 int 型別, 而 setPrice() 的參數型別也不同。但讀者可自行嘗試, 不管 Bicycle 類別物件是用 double 或 int 呼叫 setPrice() 成員函式, 都會出現編譯錯誤, 一定要指定函式所屬的類別才能編譯成功。 存取同名的成員 因為 Bicycle 類別中的 howMuch() 成員函式傳回的是 Vehicle::price 這個成員, 而第 46 行設定的價格是 ExerciseTool 中的 price, 所以最後顯示的價格仍是 2999。 同一個基礎類別的多重繼承 在多重繼承時, 子類別會繼承到所有父類別各自的成員。那麼假設類別 DD 多重繼承了父類別 BB 和 CC, 但 BB、CC 本身又都是從基礎類別 AA 所衍生的, 則此時 DD 中會有幾份類別 AA 的成員呢?答案是兩份, 如下圖所示: 同一個基礎類別的多重繼承 同一個基礎類別的多重繼承 我們可用以下的範例程式驗證之: 同一個基礎類別的多重繼承 同一個基礎類別的多重繼承 如執行結果所示, BB、CC 類別分別繼承了了一份 AA 類別的資料成員 i, 而多重繼承的 DD 則又從 BB 和 CC 各繼承一份資料成員 i, 所以對 DD 來說, 它共有兩個資料成員 i, 要存取時, 需如前面所提繼承同名成員一樣, 要用範圍解析運算子標示出要使用的是來自何處的成員。 同一個基礎類別的多重繼承 同一個基礎類別的多重繼承 存取成員函式時
原创力文档

文档评论(0)