详解虚函数的实现过程之多重继承(3)
下面來一起探索一下多重繼承時,有虛函數會怎么繼承呢?
這里大家猜一下,SofaBed會占多少個字節呢?
首先我們是不是得猜一下它有幾個虛表指針?
4* 4(4個int數據)+2*4(兩個虛表指針)=24(字節)
那我們一起接下來去驗證一下為什么會有兩個虛表指針呢?沖沖沖!!!
是不是感覺很神,對吧?兩個虛表指針,它怎么排兵布陣的呀?這得接下來一起看看嘍:
從上面表格可以看出它所繼承的父類中各自有一個指針,查了一下指針指向的地方,分別是0x426198與0x42501C,接下來我們去看看這兩個地方所存在的數據:
啊這,這兩個虛表里面保存了子類的虛函數和父類的虛函數,父類的虛函數都是在子類中沒有實現的。編譯器將CSofaBed的虛函數制作了兩份,為什么會有兩份呢?那我們是不是應該去看看構造函數呢?對吧,接下來往下沖:
它首先調用了父類CSofa的構造函數(地址401205處)。在調用另一個父類CBed時,并不是直接將對象的首地址作為this指針傳遞,而是向后調整了父類CSofa的長度(this+8)(ecx為什么加8呢?因為虛表指針占4字節,m_nColor占四字節,所以就加8),以調整后的地址值作為this指針,最后再調用CBed(地址40120A~地址401214)的構造函數。。
由于有了兩個父類,因此子類在繼承時也將他們的虛表指針一起繼承過來,也就有了兩個虛表指針。可見,在多重繼承中,子類虛表指針的個數取決于所繼承父類的個數,有幾個父類便會出現幾個虛表指針。(虛基類除外,這個另討論)
當虛表指針在將子類對象轉換成父類指針使用時,每個虛表對應著一個父類,使用方法也就有點不太一樣,我們一起看來看看吧:
當子類對象轉換成不同的父類指針時,調整的位置也就不一樣,當在轉換成CSofa指針時,會直接取對象首地址,當在轉換成CBed指針時,會調整首地址(地址40F747處)并跳過第一個父類所占用的空間(占用8字節,所以+8)。
下面我們來看看它的析構函數是怎么樣的:
由于具有多個父類(多個同時繼承的父類),因此子類中產生了多個虛表指針。
在對父類進行析構時,需要設置this指針,用于調用父類的虛構函數。由于具有多個父類,當在析構的過程中調用各個父類的析構函數時,傳遞的首地址將有所不同,編譯器會根據每個父類在對象中所占用的空間位置,對應地傳入各個父類的首地址作為this指針。
總結如下:
單繼承類:
a.在類對象占用的內存空間中,只保存一份虛表指針
b.由于只有一個虛表指針,對應的也只有一個虛表
c.虛表中各項保存了類中各虛函數的首地址
d.構造時先構造父類,再構造父類,并且只調用一次父類構造函數
e.析構時先析構自身,再析構父類,并且只調用一次父類析構函數
多繼承類:
a.在類對象所占用的內存空間中,根據繼承父類的個數保存對應的虛表指針
b.根據所保存的虛表指針的個數,對應產生相應個數的虛表
c.轉換父類構造函數,需要調整到對象的首地址
d.構造時需要調用多個父類構造函數
e.構造時先構造繼承列表中第一個父類,然后依次調用最后一個繼承的父類構造函數
f.構造時先構造繼承列表中的第一個父類,然后依次調用到最后一個繼承的父類構造函數
g.析構時先析構自身,然后以構造函數相反的順序調用所有父類的析構函數。
h.當對象作為成員時,整個類對象的內存結構和多重繼承很相似。當類中無虛函數時,整個類對象的內存結構和多重繼承完全一樣,可酌情還原;當父類或成員對象存在虛函數時,通過觀察虛表指針的位置和構造函數,析構函數中填寫虛表指針的數目以目標地址,來還原繼承或成員關系
虛函數系列:
詳解虛函數的實現過程之初探虛表(1)
詳解虛函數的實現過程之單繼承(2)
詳解虛函數的實現過程之多重繼承(3)
詳解虛函數的實現過程之虛基類(4)
詳解虛函數的實現過程之菱形繼承(5)
總結
以上是生活随笔為你收集整理的详解虚函数的实现过程之多重继承(3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 详解虚函数的实现过程之单继承(2)
- 下一篇: 详解虚函数的实现过程之虚基类(4)