C++_虚继承_虚函数_纯虚函数(多继承的二义性,多态)
基本信息
每一個類都有一個虛表,以及虛表指針; 虛表的內(nèi)容是編譯器決定的,虛表中用于存放虛函數(shù)的指針, 程序運行時的類型信息等;
每個多態(tài)對象都存放著一個指向當前類型的虛表的指針, 該指針在構(gòu)造函數(shù)中被賦值, 一般來說當調(diào)用當前這個類的構(gòu)造函數(shù), 則虛表指針就指向當前類的虛表
虛繼承
用于解決多重繼承的過程中成員訪問的二義性(菱形繼承)
格式:class 類名 : virtual 繼承方式 基類類名
注:在虛繼承的過程中,編譯器會為子類創(chuàng)建一個虛表,以及一個虛基表指針(占用對象空間)指向虛表(不占用對象空間)
例:
上述代碼中: B, C虛繼承于A, 他們二者”共享”一份A(用集合的方式理解,A屬于B, C的交集), 當D繼承B,C的時候只會保留一份A在D中
若未采用虛繼承在D創(chuàng)建對象的時候,會創(chuàng)建2份的A,出現(xiàn)冗余, 使用虛繼承繼承基類的兩個虛指針,并調(diào)整虛指針與虛基類首地址的偏移量,使得繼承過程中只保留一份的A
當使用虛繼承的過程中,虛基類被共享,無論繼承多少次,虛表指針都只會指向一份的虛基類,對象模型中只會存有一份的虛基類對象
虛函數(shù)
在基類中使用virtual修飾的成員函數(shù),當函數(shù)聲明為虛函數(shù)時,告訴編譯器不要靜態(tài)鏈接到該函數(shù),而是根據(jù)程序的運行過程中動態(tài)地根據(jù)該對象類型來調(diào)用函數(shù), 就所謂的多態(tài)
注:當基類成員函數(shù)聲明為virtual, 子類進行對virtual重寫,那么重寫后的函數(shù)都為虛函數(shù)(即使該函數(shù)前面沒寫virtual關(guān)鍵字); 虛函數(shù)成員的virtual關(guān)鍵字只能出現(xiàn)在類中定義的函數(shù)原型前面, 不能出現(xiàn)在類外成員函數(shù)實現(xiàn)的前面
虛函數(shù)一般不聲明為inline函數(shù), inline函數(shù)屬于靜態(tài)綁定, 而虛函數(shù)的調(diào)用是動態(tài)綁定, 如果將虛函數(shù)作為inline函數(shù)也不會出錯
構(gòu)造函數(shù), 靜態(tài)函數(shù), 復(fù)制構(gòu)造函數(shù)不可做為虛函數(shù), 原因只在于虛函數(shù)為動態(tài)綁定;
此時,編譯器看的是指針的內(nèi)容(決定了能夠調(diào)用哪些虛函數(shù)),它指向的對象類型決定了該調(diào)用誰的虛函數(shù)。因此,由于 tri 和 rec 類的對象的地址存儲在 *shape 中,所以會調(diào)用各自的 area() 函數(shù)。
動態(tài)綁定的底層實現(xiàn):
虛表指針需要初始化才能調(diào)用虛函數(shù),虛表指針在構(gòu)造對象的時候初始化(初始化順序與構(gòu)造函數(shù)的調(diào)用順序一致),當構(gòu)造函數(shù)發(fā)現(xiàn)BASE具有虛函數(shù),虛指針指向BASE的虛表中的虛函數(shù),當執(zhí)行構(gòu)造子類對象的時候,子類中虛指針指向子類虛表中的虛函數(shù);當對象創(chuàng)建好后,虛表指針指向的是子類的虛函數(shù),從而對象調(diào)用虛函數(shù)時,實現(xiàn)多態(tài)
注:虛表會被繼承,當子類重寫虛函數(shù)的時候,那么虛表中的虛函數(shù)地址則會改變
在動態(tài)分配內(nèi)存的時候,析構(gòu)函數(shù)必須是虛函數(shù)(利用動態(tài)綁定)防止不會調(diào)用所需的析構(gòu)函數(shù)
使用虛函數(shù)意味著多態(tài),多態(tài)必須具備的三個條件:繼承關(guān)系; 繼承的過程中必須有同名的虛函數(shù); 存在基類的指針或引用,通過該指針或引用調(diào)用虛函數(shù)
虛析構(gòu)函數(shù)
析構(gòu)函數(shù)可作為虛函數(shù), 方便父類指針知道該調(diào)用哪個子類的析構(gòu)函數(shù)(析構(gòu)函數(shù)的多態(tài))
一般情況下, 如果涉及到多態(tài), 則將析構(gòu)函數(shù)設(shè)置為virtual
//上述代碼輸出:父類析構(gòu)函數(shù),由于BASE指針偏移量的問題,未將父類析構(gòu)函數(shù)設(shè)置為虛析構(gòu)函數(shù),導(dǎo)致靜態(tài)綁定只會釋放父類內(nèi)存區(qū),不會釋放子類內(nèi)存區(qū),導(dǎo)致內(nèi)存泄漏;
為了避免這種錯誤應(yīng)將父類,子類的析構(gòu)函數(shù)設(shè)置為virtual,則會解決這個問題
注: 子類對象析構(gòu)函數(shù)的調(diào)用順序,先調(diào)用子類析構(gòu)函數(shù), 然后調(diào)用父類析構(gòu)函數(shù), 調(diào)用順序與構(gòu)造函數(shù)調(diào)用順序相反
純虛函數(shù) (抽象類)
在基類重定義純虛函數(shù),以便在派生類中重新定義該函數(shù)來適用于對象,純虛函數(shù)就相當于接口,用于規(guī)范派生類行為
包含純虛函數(shù)的類是抽象類,不能實例化, 當子類繼承抽象類時,若沒有實現(xiàn)純虛函數(shù), 則子類還是抽象類
語法: virtual void function()=0;
等于0表示沒有函數(shù)體,
注: C++中,父類中的純虛函可以有實現(xiàn)方式, 但是編譯器會忽略, 父類依舊為抽象類, 由于抽象類不能實例化, 但是可以定義指針或引用, 通過指針和引用依舊能實現(xiàn)多態(tài), 與Java中抽象類實現(xiàn)多態(tài)的方式一致
總結(jié):
- 虛繼承: 虛表基表, 虛基指針主要用來記錄偏移量(虛基指針在虛基表上),以保證多繼承的過程中只復(fù)制一份的基類
- 虛函數(shù): 虛表,虛指針主要用于指向虛函數(shù)(虛指針指向虛表中的虛函數(shù)地址)
- 純虛函數(shù): 就是接口, 規(guī)范子類行為
總結(jié)
以上是生活随笔為你收集整理的C++_虚继承_虚函数_纯虚函数(多继承的二义性,多态)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python写xml文件_用python
- 下一篇: github page hexo博客gi