俗話說,一千個讀者就有一千個哈姆雷特。多型就是不同物件去完成某個行為,會產生不同的狀態。這就類似於多型,不同的物件對完成同乙個行為會表現出不同的狀態。
1、多型的構成條件
必須通過基類的指標或者引用呼叫虛函式;
被呼叫的函式必須是虛函式(有virtual關鍵字);
派生類必須對基類的虛函式進行重寫(函式名、返回值型別、引數列表均相同)。
這三者缺一不可,若要實現多型必須要滿足這三個條件。
舉個栗子
class
person
//基類};
class
seniorcard
:public person//派生類};
void
func
(person & people)
//①:通過基類的引用呼叫虛函式
2、虛函式的定義
虛函式就是有virtual關鍵字的類成員函式。
class
person
};
3、虛函式的重寫
虛函式的重寫:派生類中有乙個跟基類完全相同的虛函式(即派生類虛函式與基類虛函式的返回值型別、函式名、引數列表完全相同),稱派生類的虛函式重寫了基類的虛函式。
理解隱藏、重寫、過載的區別請移步另一篇部落格:隱藏、重寫、過載的區別
注意事項:在重寫基類虛函式時,派生類的虛函式可以不加virtual關鍵字,這樣是正確的,可以構成重寫,但是這樣的寫法並不規範(因為派生類繼承基類的虛函式後依然保持虛函式的特性,不加virtual會導致可讀性變差),所以派生類的虛函式需要加上virtual
。
虛函式重寫的兩個特例
1、協變
協變是基類與派生類函式的返回值型別不同(函式名與引數列表相同),即基類虛函式返回基類物件的指標或者引用,派生類虛函式返回派生類物件的指標或者引用時,稱為協變。
舉個栗子
classa;
classb:
public a
;class
person};
class
seniorcard
:public person
};
2、析構函式的重寫基類與派生類的析構函式名字不同。如果基類的析構函式為虛函式,此時派生類析構函式只要定義,無論是否加virtual關鍵字,都與基類的析構函式構成重寫。(所有析構函式在底層的函式名是相同的)
虛析構函式是用來解決子類指標轉化為父類指標進行析構的問題。
4、override 和 final
1、override
override用來檢查派生類虛函式是否重寫了基類某個虛函式,如果沒有重寫編譯報錯。
2、final
final用來修飾虛函式,表示該虛函式不能再被繼承。
抽象類就是包含純虛函式的類,並且抽象類不能定義物件。
純虛函式:虛函式 = 0就成為純虛函式
純虛函式的宣告語法
virtual
void
func()
=0;//表示純虛函式
純虛函式禁止對抽象類呼叫函式用值傳遞的方式(可以採用指標或引用呼叫),這是防止物件切片,所以純虛函式存在的意義就是在編譯期間避免物件切片。解釋:物件切片就是指將物件分割之後剩下與目的型別相匹配的子物件
實現繼承與介面繼承
一般函式的繼承是實現繼承,派生類繼承了基類,可以使用函式,繼承的是函式的實現。
虛函式的繼承是介面繼承,派生類繼承的是基類函式的介面,目的是進行重寫,達到多型,若不用實現多型,即不用將函式定義為虛函式。
1、虛函式表
虛函式表簡單來說就是存放虛函式位址的一張表(只要有虛函式就存在虛表)
那麼虛表的本質就是存放虛函式指標的指標陣列,陣列最後放了乙個nullptr.
用乙個簡單的例子來說明虛函式表
#include
using
namespace std;
class
base
virtual
void
fun2()
void
fun3()
};class
test
:public base};
intmain()
由上面這個例子可以看出:
1、基類物件b中有乙個虛表指標,存放虛函式fun1()和fun2()的位址;派生類物件t中也有乙個虛表指標,用來存繼承基類的虛表指標和自己的虛表指標,由於派生類對fun1()進行了重寫,所以派生類中存放自己的虛函式fun1()的位址和基類虛函式fun2()的位址。總之,虛函式是用來存放虛函式指標的。
2、關於派生類中虛表的產生:
總結:
虛表是乙個二級的函式指標,只要類中包含虛函式,就會在物件的頭部包含乙個4位元組虛表指標(vfptr);
虛表用來存放虛函式指標,而虛表和虛函式都存放在**段,物件中存放的是虛表指標;
2、多型的原理
呼叫虛函式
3、多型的分類
多型可分為靜態多型與動態多型。
靜態多型又稱為靜態繫結/前期繫結/早繫結,在程式編譯期間已經確定了程式的行為。比如:函式過載
動態多型又稱為動態繫結/後期繫結/晚繫結,在程式執行期間才能確定程式的行為以及呼叫的函式。
1、單繼承
單繼承就是乙個派生類只能繼承乙個基類。
單繼承的虛函式表
派生類的虛表中包括自己的虛函式指標和基類的虛函式指標,若派生類函式對基類函式進行重寫,則派生類中的虛表指標將其覆蓋。
2、多繼承
多繼承就是乙個派生類可以繼承多個基類,包含所有基類的內容。
多繼承的書寫格式
class
test
:public base1,
public base2
;
菱形繼承通俗來講就是:某個類的兩個基類來自同乙個基類,例如下面這幅圖:
子類student和子類teacher中都繼承了其父類person中的所有內容,當子類assistant同時繼承student和teacher,則會有兩份person的內容,若不指定父類,則會出現範圍不明確的問題(冗餘性、二義性)
冗餘性:出現兩份相同的內容
二義性:不明確某個內容是繼承自哪個類
解決辦法:虛繼承
虛繼承虛繼承的作用就是來解決菱形繼承產生冗餘性和二義性的問題,且只能用在菱形繼承中。
對虛繼承的解釋:含有乙個虛基類指標(vbptr),指向自己的基類,作用是描述自己的父類。當發現被繼承的另乙個父類中也有這麼乙個相同的虛基類時,兩個基類會合併,只保留乙個。
虛繼承的書寫格式:
//兩種寫法均可,與順序無關
class
test
:virtual
public base
;class
test
:public
virtual base
;
c 多型總結
多型 多型可以簡單地概括為 乙個介面,多種方法 程式在執行時才決定呼叫的函式,它是物件導向程式設計領域的核心概念。接下來,我寫乙個簡單地函式來說明多型 includeusing namespace std int add int left,int right float add float left...
C 多型總結
多型繫結分兩種情況,一種是靜態繫結即編譯時多型,一種是動態繫結即執行時多型是利用過載實現的。對於非虛函式的成員來說,系統在編譯時,按照函式的引數的區別來繫結要實現的操作,在編譯時就確定了呼叫哪個函式。簡單地說,虛函式是動態繫結的基礎 動態繫結是實現執行時多型的基礎。要觸發動態繫結,需滿足兩個條件 1...
C 多型總結
多型概念 同乙個事物在不同環境下具有不同的狀態 虛函式概念 在函式返回值前加上 virtual 關鍵字 多型分類 靜態多型 早繫結 在編譯器編輯時確認要呼叫的函式 1 函式過載 2 泛型程式設計 動態多型 晚繫結 在程式執行時確認將要呼叫的函式 1 基類中存在虛函式 2 繼承當中對基類進行重寫並且 ...