多型
基類指標
// 父類指標可以 new 一個子類物件
Human *pman = new Man();
Human *pwman = new Wonan();
丟擲問題:父類指標沒有辦法呼叫子類的成員函式,那麼你為什麼還讓父類指標 new 一個子類物件呢?
下面與虛擬函式搭配
虛擬函式(動態繫結)
我們只定義一個物件指標,就能夠呼叫父類,以及各個子類的同名函式? ===> 有,這個物件指標,它的型別必須是父類型別
對這個函式有要求:
- 父類中,函式宣告之前要加 virtual 宣告成虛擬函式
- 一旦某個函式(在基類)被宣告成了虛擬函式,那麼所有派生類(子類)中同名函式都是虛擬函式
Human *phuman = new Men();
phuman->eat(); // 呼叫的是 Men 類的 eat 函式
delete phuman; // new 的物件使用完之後要 delete,防止記憶體洩漏
phuman = new Women();
phuman->eat(); // 呼叫的是 Women 類的 eat 函式
delete phuman;
phuman = new Human();
phuman->eat(); // 呼叫的是 Human 類的 eat 函式
delete phuman;
override
注意:為了避免你在子類中寫錯虛擬函式,在 C++11 中,你可以在函式宣告這裡增加一個 override 關鍵字
這個關鍵字在 "子類" 中,而且是子虛擬函式專用
override就是用來說明派生類中的虛擬函式,你用了這個關鍵字之後,編譯器就會認為你這個 函式就是覆蓋了父類中的同名函式。
只有虛擬函式才存在子類可以覆蓋父類中同名函式的問題,那麼編譯器就會在父類中找同名同參的虛擬函式,如果沒找到,編譯器就會報錯、
final
final 也是虛擬函式專用,是用在父類,如果我們在父類的函式宣告中加了 final,那麼任何嘗試覆蓋該函式的操作都將引發錯誤。
virtual void eat() final;
多型性
系統內部實際上是要查一個虛擬函式表,找到 eat() 的入口地址,從而呼叫父類或者子類的 eat() 函式,這就是執行時期的多型性
純虛擬函式【類似 Java 中的介面 interface】===> 函式原型後增加 = 0
純虛擬函式:沒有函式體,只有一個函式宣告
virtual void eat() = 0;
是在基類中宣告的虛擬函式,但是它在基類中沒有定義,但是要求任何派生類都要定義該虛擬函式自己的實現方法
注意:一旦一個類中有純虛擬函式了,那麼你就不能生成這個類的物件了【Java 中 interface 不能生成物件】
抽象類不能用來生成物件,主要目的用來統一管理子類物件
基類的解構函式一般寫成虛擬函式(虛解構函式)
// 1. 宣告一個 men 物件
Men men;
// 2. 寫法2:使用 new
Men *pmen = new Men();
delete pmen; // 自己 new 的物件,必須使用 delete 呼叫解構函式
有一個問題
Human *phuman = new Men(); // 父類指標指向 子類物件
delete phuman; // 沒有執行子類的解構函式
結論:用基類指標 new子類的物件,在 delete 的時候,系統不會呼叫派生類的解構函式,這肯定就有問題了
解決方式:
- 父類的解構函式寫成虛擬函式
- 繼承時候使用 public