天天看點

多态多态的原理:

什麼是多态?簡單說就是完成某個動作時不同對象會産生不同的狀态。見人說人話,見鬼說鬼話。

多态是建立在繼承基礎上的

實作多态的兩個條件:

1.在基類中定義虛函數(virtaul),并在派生類對其重寫。

2.用基類對象的指針或引用調用虛函數。

滿足這兩個條件時,通過傳遞不同類型的對象就會調用對應的虛函數,也就完成了多态的形式。

以下是對上面多态兩個條件的解釋:

1.虛函數即用virtaul關鍵字修飾的成員函數,這裡要注意是成員函數,即不可以是友元函數或是普通函數,虛函數的概念隻存在于類作用域中。那麼若在基類中将一個成員函數設定為虛函數,在子類中必須将其重寫,否則就沒有将它設定成虛函數的意義。

重寫的意思是在派生類中将基類中繼承到子類的同名虛函數函數體重新定義,以此完成不同的功能,這裡同名必須是子類重寫的意思是在派生類中将基類中繼承到子類的同名虛函數函數體重新定義,以此完成不同的功能,這裡同名必須是子類與基類的傳回值、函數名、參數清單完全相同,缺一則不構成重寫。在子類中的重寫可以不加virtual關鍵字修飾,但不建議這麼做。若基類中的成員函數沒有virtual繼承到子類并對其函數體進行重新定義則也不構成重寫,這裡隻是基類和子類的同名隐藏(函數名相同即構成同名隐藏)。通過這我們可以看出構成重寫的要求更加嚴格。

重寫對函數體内的内容是否修改并無要求,但一般都是要與基類中函數體内容不同,否則重寫的意義就不存在了。

2.為什麼要用基類的指針或引用調用虛函數呢?

在編譯階段無法知道使用哪個類的虛函數,隻有在程式運作時通過實參的類型确定調用哪一個類的虛函數。

若不用基類的指針或引用作為實參而是按照值的方式進行傳參,那麼在編譯時就會生成一個基類的臨時對象,是以無論傳遞哪個類的對象調用的都是基類的虛函數。則不構成多态。

下面是一個簡單的實作多态的代碼:

兩個對象分别調用了自己類中的TestFunc函數。

虛函數重寫的兩個例外:(以下兩種方式也構成重寫)

1.協變(基類與派生類虛函數傳回值類型不同):基類虛函數傳回基類對象的指針或者引用,派生類虛函數傳回派生類對象的指針或者引用時

析構函數的重寫(基類與派生類析構函數的名字不同)

如果基類的析構函數為虛函數,此時派生類析構函數隻要定義,無論是否加virtual關鍵字,都與基類的析構函數構成重寫,雖然基類與派生類析構函數名字不同。雖然函數名不相同,看起來違背了重寫的規則,其實不然,這裡可以了解為編譯器對析構函數的名稱做了特殊處理,編譯後析構函數的名稱統一處理成destructor。

C++11中override關鍵字寫在派生類虛函數後檢測這個函數是否是子類中虛函數的重寫,沒有則編譯報錯。final關鍵字修飾虛函數表示該虛函數不能再被繼承。

在設計問題時有時在基類中實作一個函數不能具體下來,是以基類中的這個虛函數就無法實作,隻有在子類中才能确定下具體的方法,是以我們需要創造一個純虛函數來友善在子類中實作。純虛函數即在虛函數的後面寫=0,包含純虛函數的類叫做抽象類,抽象類不能創造對象,派生類繼承後也不能創造對象,隻有對純虛函數重寫後派生類才能創造出對象,純虛函數規範了派生類必須重寫,展現了結構繼承。但可以建立抽象類的指針。

看看下面的例子:

輸出結果:

多态多态的原理:

以下說明都是在32位編譯環境下在VS2013的測試

一、虛函數表

假設一個類中隻有一個成員變量和一個虛函數,那麼這個類的大小是多少呢?通過sizeof可以看到是8個位元組,那麼再增加一個虛函數呢?計算節後還是8個位元組

多态多态的原理:

通過監視視窗可以看到這多出來的四個位元組存放的是一個虛表指針,一個含有虛函數的類中至少含有一個虛表指針,因為虛函數的位址存放在虛表中,通過虛函數指針才能通路到虛表。虛函數才會存在于虛表中。

派生類中又是怎麼樣的呢?我們繼續往下看:

多态多态的原理:

首先要明确的是派生類與基類的虛表指針不同,D類繼承自B類并對TestFun1()函數進行了改寫,并新增加了TestFun5()和TestFun5()兩個函數,是以派生類的虛表中存放的是這樣的内容:

多态多态的原理:

是以我們得出結論:

1.派生類繼承下基類的成員變量,新增派生類指針将基類虛表的内容拷貝一份,若在派生類中重寫某個虛函數,則用重寫後的函數将之前的對應的虛函數替換(相同偏移量),覆寫就是指虛表中虛函數的覆寫。重寫是文法的叫法,覆寫是原理層的叫法,沒有重寫的虛函數不做修改,派生類新增的虛函數按照生命順序依次放到虛表的後面。

2.虛函數表本質是一個存虛函數指針的指針數組,這個數組最後面放了一個nullptr。

3.注意虛表存的是虛函數指針,不是虛函數,虛函數和普通函數一樣的,都是存在代碼段的,隻是他的指針又存到了虛表中。另外對象中存的不是虛表,存的是虛表指針。那麼虛表存在哪的呢?實際我們去驗證一下會發現vs下是存在代碼段的

二、多态的原理

多态多态的原理:

通過函數TestFun3()函數的反彙編代碼,通過它我們可以看到多态到底是怎麼工作的,首先前兩個語句是從對象中的前4個位元組取虛表的位址,第四條語句通過ecx寄存器傳遞this指針,第五條語句是從虛表中通過偏移量找到對應的虛函數,第六條語句就是調用虛函數了。是以通過傳遞不同的對象(this指針不同)就可以找到對應的虛函數,即實作了多态的過程。

基類指針指向派生類對象:

is-a的關系使基類指針可以指向派生類對象,但是在這個過程中基類指針隻能通路基類的成員變量和成員函數,若想通路子類中的成員變量和成員函數則需要強制類型轉化但是是一種潛在的危險操作,注意:如果在基類和派生來中定義了虛函數(通過繼承和重寫),并通過基類指針在派生類對象上調用這個虛函數,則實際調用的是這個函數的派生類版本。

c++

繼續閱讀