這一篇介紹一下 C++ 面向對象三大特征之一的多态(之前面試某大廠的實習生被問到多态,後來又了解到一些設計模式,才體會到多态的強大,在這裡把對多态的一點點淺顯認識總結一下)
如有侵權,請聯系删除,如有錯誤,歡迎大家指正,謝謝多态
- 父類的一個指針,可以有多種執行狀态(父類的指針調用子類的函數),即多态
- 多态實際上隻是一種思想,而虛函數是實作這個思想的文法基礎
虛函數
虛表- 若對象有虛函數,對象空間最開始 4Byte(32Bit目标平台)或 8Byte(64bit目标平台)内容是虛表(虛函數清單)的首位址,叫虛指針
- 在執行個體化對象時,編譯器檢測到虛函數(virtual修飾的成員函數)時,會将虛函數的位址放到虛表(類似于一個存放函數指針的數組)中
- 當執行個體化子類時,檢測到有虛函數的重寫,編譯器會用子類重寫的虛函數位址覆寫掉之前父類的虛函數位址,當調用虛函數時,檢測到函數是虛函數就會從虛表中找對應的位置調用,若子類沒有重寫,虛表中的虛函數位址就還是父類的,若子類中有重寫,虛表記錄的就是子類重寫的虛函數位址,即實作了父類的指針調用子類的函數
- 虛表中先記錄父類中的虛函數位址,接着記錄子類中虛函數位址(若子類重寫父類的虛函數則是覆寫)
- 最後虛表還有一個尾值是 0
class
虛函數 - 在一般成員函數前面加 virtual 即可将成員函數聲明為虛函數
class
- 在單一 class 中實作虛函數意義并不大,虛函數主要是為了實作子類函數重寫父類函數的作用
- 要實作多态,通常父類中的虛函數與子類中的函數的傳回值類型、函數名和參數清單必須都相同的,但是在協變的情況下傳回值類型可以不一樣,協變即虛函數的傳回值類型為所在類的指針或引用
// ====== 一般多态的實作 ======
- 子類重寫的函數預設是虛函數,也可以顯式的加上 virtual,也可以不加
- 虛函數不能是内聯函數,加上 inline 是沒有效果的
- 構造函數不能是虛函數
- 析構函數可以是虛函數(在多态中應寫虛析構)
class
純虛函數 - virtual void fun() = 0; // 這是純虛函數的形式
- 純虛函數可以沒有函數實作,有純虛函數的類不能執行個體化對象,繼承有純虛函數的父類的子類必須在子類中實作它,子類才能執行個體化對象,如果不在子類中實作它,子類也不能執行個體化對象
- 抽象類,有純虛函數的類就是抽象類
- 接口類,除資料成員和構造函數外,其餘全是純虛函數的類,子類繼承接口類必須實作全部的純虛函數
- 構造函數不可以是純虛函數
class
虛析構 - 在多态中,如果釋放父類指針(指向子類的父類指針),隻會調用父類的析構函數,将父類的析構函數聲明為虛函數(虛析構,加 virtual 修飾的析構函數),就會先調用子類的析構函數再調用父類的析構函數,是以在多态中,要用虛析構
- 父類的析構函數加了 virtual 修飾,delete 會調用子類和父類的析構函數,子類可以顯式的加 virtual ,也可以不加, 預設是有的 virtual
- 還有一點需要注意的,delete 誰的指針就會調用誰的析構函數