1、空類,空類單繼承,空類多繼承的sizeof
#include
using namespace std;
class base1
;class base2
;class derived1:
public base1
;class derived2:
public base1,
public base2
;int main()
結果為:
sizeof(base1) = 1 sizeof(b1) = 1
sizeof(base2) = 1 sizeof(b2) = 1
sizeof(derived1) = 1 sizeof(d1) = 1
sizeof(derived2) = 1 sizeof(d1) = 1
可以看出所有的結果都是1。
2、含有虛函式的類以及虛繼承類的sizeof
虛函式(virtual function)是通過一張虛函式表(virtual table)來實現的。編譯器必需要保證虛函式表的指標存在於物件例項中最前面的位置(這是為了保證正確取到虛函式的偏移量)。
假設我們有這樣的乙個類:
class base
virtual void g()
virtual void h()
};當我們定義乙個這個類的例項,base b時,其b中成員的存放如下:
指向虛函式表的指標在物件b的最前面。
虛函式表的最後多加了乙個結點,這是虛函式表的結束結點,就像字串的結束符「\0」一樣,其標誌了虛函式表的結束。這個結束標誌的值在不同的編譯器下是不同的。在winxp+vs2003下,這個值是null。而在ubuntu 7.10 + linux 2.6.22 + gcc 4.1.3下,這個值是如果1,表示還有下乙個虛函式表,如果值是0,表示是最後乙個虛函式表。
因為物件b中多了乙個指向虛函式表的指標,而指標的sizeof是4,因此含有虛函式的類或例項最後的sizeof是實際的資料成員的sizeof加4。
下面將討論針對基類含有虛函式的繼承討論
(1)在派生類中不對基類的虛函式進行覆蓋,同時派生類中還擁有自己的虛函式,比如有如下的派生類:
class derived: public base
virtual void g1()
virtual void h1()
};基類和派生類的關係如下:
當定義乙個derived的物件d後,其成員的存放如下:
可以發現:
1)虛函式按照其宣告順序放於表中。
2)父類的虛函式在子類的虛函式前面。
此時基類和派生類的sizeof都是資料成員的sizeof加4。
(2)在派生類中對基類的虛函式進行覆蓋,假設有如下的派生類:
class derived: public base
virtual void g1()
virtual void h1()
};基類和派生類之間的關係:其中基類的虛函式f在派生類中被覆蓋了
當我們定義乙個派生類物件d後,其d的成員存放為:
可以發現:
1)覆蓋的f()函式被放到了虛表中原來父類虛函式的位置。
2)沒有被覆蓋的函式依舊。
這樣,我們就可以看到對於下面這樣的程式,
base *b = new derive();
b->f();
由b所指的記憶體中的虛函式表的f()的位置已經被derive::f()函式位址所取代,於是在實際呼叫發生時,是derive::f()被呼叫了。這就實現了多型。
(3)多繼承:無虛函式覆蓋
假設基類和派生類之間有如下關係:
對於子類例項中的虛函式表,是下面這個樣子:
我們可以看到:
1) 每個父類都有自己的虛表。
2) 子類的成員函式被放到了第乙個父類的表中。(所謂的第乙個父類是按照宣告順序來判斷的)
由於每個基類都需要乙個指標來指向其虛函式表,因此d的sizeof等於d的資料成員加3*4=12。
(4)多重繼承,含虛函式覆蓋
假設,基類和派生類又如下關係:派生類中覆蓋了基類的虛函式f
下面是對於子類例項中的虛函式表的圖:
我們可以看見,三個父類虛函式表中的f()的位置被替換成了子類的函式指標。這樣,我們就可以任一靜態型別的父類來指向子類,並呼叫子類的f()了。如:
derive d;
base1 *b1 = &d;
base2 *b2 = &d;
base3 *b3 = &d;
b1->f(); //derive::f()
b2->f(); //derive::f()
b3->f(); //derive::f()
b1->g(); //base1::g()
b2->g(); //base2::g()
b3->g(); //base3::g()
3、乙個關於含虛函式及虛繼承的sizeof計算
#include
using namespace std;
class base
;class derived1:
public base
;class derived2:
public base
;class derived3:virtual public base
;class derived4:virtual public base
;class derived5:virtual public base
;class derived6:virtual public base
;int main()
對於base, derived1和derived2的結果根據前面關於繼承的分析是比較好理解的,不過對於虛繼承的方式則有點不一樣了,根據結果自己得出的一種關於虛繼承的分析,如對derived3或derived4定義乙個物件d,其裡面會出現三個跟虛函式以及虛繼承的指標,因為是虛繼承,因此引入乙個指標指向虛繼承的基類,第二由於在基類中有虛函式,因此需要指標指向其虛函式表,由於派生類自己本身也有自己的虛函式,因為採取的是虛繼承,因此它自己的虛函式不會放到基類的虛函式表的後面,而是另外分配乙個只存放自己的虛函式的虛函式表,於是又引入乙個指標,從例子中看到derived5和derived6的結果是8,原因是在派生類要麼沒有自己的虛函式,要麼全部都是對基類虛函式的覆蓋,因此就少了指向其派生類自己的虛函式表的指標,故結果要少4。(這個是個人的分析,但原理不知道是不是這樣的)
虛函式及繼承
1 空類,空類單繼承,空類多繼承的sizeof include using namespace std class base1 class base2 class derived1 public base1 class derived2 public base1,public base2 int m...
虛函式 虛繼承
include using namespace std class a class b public a class c public b int main 結果是 4,4,4 為什麼?一,在private,protect,public的實際繼承中,派生類和基類擁有相同的虛函式表。但如果是虛繼承,會...
虛函式,虛繼承
1 空類,空類單繼承,空類多繼承的sizeof include using namespace std class base1 class base2 class derived1 public base1 class derived2 public base1,public base2 int m...