1. 程式人生 > >C++中的覆蓋、多態

C++中的覆蓋、多態

基本類型 ast his 指針成員 如果 class 類型 構造 返回值

一、虛函數、覆蓋、多態
虛函數:成員函數在定義時添加了 virtual 關鍵字,這種函數叫虛函數
覆蓋:如果在子類中實現與父類中的虛函數具有相同的函數,那麽子類中的成員函數會覆蓋父類中的成員函數
多態:如果子類中的成員函數對父類中的成員進行了覆蓋,當一個指向子類的父類指針或引用了子類的父類引用,使用它調用虛函數,然後根據實際的調用對象調用子類中的覆蓋函數,而不是父類中的虛函數,這種語法現象叫多態

多態的意義在於,同一種類發出同一種調用,而產生不同的反應


二、覆蓋、重載、隱藏
滿足覆蓋(重寫)的條件:
a、必須是成員函數
b、必須是虛函數
c、函數簽名必須相同
d、如果返回值是基本類型則必須相同(否則會產生編譯錯誤)
e、如果是類類型則必須是父子類關系的指針或引用(必須能進程自動類型轉換)

滿足重載的條件:
a、必須在同一作用域下
b、函數名相同但是參數列表不同
c、const屬性相同
d、返回值的類型不會影響重載

滿足隱藏的條件:
在父子類之間名字相同的標識符,只要還構成覆蓋,則必定構成隱藏

三、多態的條件
1、多態特性除了父子類之間要構成覆蓋,還必須是父類以指針或引用的方式指向子類

2、當指針或引用已經構成多態時,此時調用成員所傳的this指針再調用成員函數時也可以構成多態

3、在子類的構造函數執行前會先調用父類的構造函數,如果調用了被覆蓋的虛函數,由於子類還沒有構造完成,因此只能是調用父類中的虛函數
構造函數在進入函數體執行時,類中看的見的資源已經全部構造完成

4、在子類的析構函數執行完成後會再調用父類的析構函數,如果調用被覆蓋的虛函數,由於子類已經析構完成已經不能算是完整的子類了,因此只能調用父類中的虛函數


四、純虛函數和抽象類
1、純虛函數
class A
{
public :
virtual void test(void) =0;
virtual void test(void) const=0;
}
a、純虛函數不需要被實現,如果非要實現也不能在類中,必須要在類外(虛函數)
b、純虛函數如果想調用必須在子類中覆蓋,然後以多態的方式調用

2、抽象類
成員函數中有純虛函數的叫抽象類,這種類不能創建對象。
如果子類繼承了抽象類,則必須把父類中的純虛函數覆蓋了,否則它也變成了抽象類不能被實體化。
因此抽象類只能以指針或引用的方式指向子類來調用自己的非純虛函數

3、純抽象類
所有的成員函數都是純虛函數,這種類叫純虛函數
面向對象的四大特性:抽象、封裝、繼承、多態
純抽象類可以是封裝類的一個過程,同時抽象類也可以當作一個統一的接口類

4、純抽象類的應用場景
回調模式:
函數a由程序員小明實現完成,如果在此時他想調用小光實現的函數b 1970年
函數b由程序員小光實現完成,小光在1980年出生
命令模式:
輸入一個指令然後執行對應的操作
生產者與消費者模式
單例模式(餓漢、懶漢)
工廠模式:
一個類以專門制造其它類為己任,這種編程模式叫工場模式
MVC模式

五、C++中的強制類型轉換
1、C語言中的強制類型轉換還能繼續使用,但是不安全
2、C++的強制類型轉換使用很麻煩,其實C++之父不建議使用強制類型轉換,一旦代碼中需要使用強制類型轉換說明代碼設計的不合理,強制類型轉換是一種亡羊補牢的方法

static_cast 靜態類型轉換
1、自健類型的強制轉換
2、void*與其它類型指針的轉換
3、父子類型之間的指針轉換

const_cast 去常類型轉換

reinterpret_cast 重解釋類型轉換
整數與指針之間的類型轉換

dynamic_cast 動態類型轉換
用於構成多態的父子類之間的指針類型轉換

六、虛函數表
1、什麽是虛函數表,當一個類中有虛函數時,編譯器會為這個分配一個表專門記錄這些虛函數,在類中會有一個隱藏的指針成員來指向這張表
2、如何證明這張表存在
有虛函數的類會比沒有虛函數的類(相同中的)多4字節,還會有添加補齊和對齊
3、一個類只有一張虛函數表,所有對象共享一張虛函數表
4、一般對象的前四個字節是指向虛函數表的指針變量

七、動態類型綁定
1、當使用父類的指針或引用指向子類時,編譯器並沒有立即生成調用函數的指針,而生成了一段代碼,用來檢查指針指向的真正的對象是什麽類型
2、在代碼真正運行時才通過對象的指針找到指向虛函數的成員指針
3、再通過成員指針訪問到虛函數表,再從中找到調用的函數地址
4、使用多態會產生額外的一些代碼和調用,因此使用多態會降低代碼的執行速度

八、類的信息
1、在C++中,使用typeid可以獲取類的一些信息,以此來確定指針指向的到底是什麽類
2、typeid可以直接使用來判斷是否是同一種類型
typeid(標識符)==typeid(標識符)
3、typeid(標識符).name() 獲取類型名,以字符串的形式呈現
4、如果typeid(指針)只能獲取到指針的類型,typeid(*指針)可以獲取到對象實際的類型信息

九、虛析構
1、如果通過父類指針或引用指向子類對象,當使用delete釋放對象時,此時只能調用父類的析構函數,如果在子類中使用new/malloc申請的內存資源,那麽將導致內存泄露

2、解決方法是把父類的析構函數設置為虛函數

3、在設計類時如果析構函數什麽都不需要做,編譯器也會生成一個空的析構函數,但這樣的話會讓繼承它的子類有安全隱患

4、最好把所有的析構函數都設置為虛函數


C++中的覆蓋、多態