虛函式及繼承

2021-06-19 11:49:38 字數 3685 閱讀 8928

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...