虛繼承與虛基類的記憶體布局

2021-07-03 14:07:40 字數 3522 閱讀 8067

1. 多重繼承的記憶體布局

struct a

;struct b:public a

;struct c:public a

struct d

多重繼承的情況, 如果父類有共同的祖父類,則祖父類物件被拷貝了多次。在該例中,b的記憶體布局為(假設從上往下為位址增加方向)

int a::a

int b: b

則d的記憶體布局為

int b::a:;a

int b:;b

int a::a

int c::c

int d::d

如果sizeof各個類的大小,很明顯

sizeof(a) = 4;

sizeof(b) = 8;

sizeof(c) = 8;

sizeof(d) = 20

如果有這樣的語句

d *pd = new d;

b *pa = pd;

c *pb = pd;

a *pa = pd;

我們分別輸出pa,pd,pc和pd的位址試試

pb = 0x8650a10

pc = 0x8650a18

pd = 0x8650a10

注意,這裡pd和pb是相同的,都指向pd的開始位置,但是pc和pb不同,pc產生了兩個4位元組的偏移,即指向d中c物件的儲存位置

pa呢,很明顯是具有二義性的,它既可以指向pb中的a,也可以指向pc中的a,自然就會報錯。所以上述語句會出現編譯錯誤。

2. 具有虛函式的多重繼承

如果多重繼承包含虛函式

struct a

};struct b: public a

};struct c: public a

};struct d: public b, public c

};

此時,a的記憶體布局為

a::vfptr

int a::a

其中vfptr為指向虛函式表的指標

那麼b的布局自然為

a::vfptr

int a::a

int b::b

d的布局為

b::a::vfptr

int b::a::a

int b::b

c::a::vfptr

int c::a::a

int c::c

int d::d

這時各類的大小為

sizeof(a) = 8;

sizeof(b) = 12;

sizeof(c) = 12;

sizeof(d) = 28;

同樣指標的位置為

pb = 0x8650a10

pc = 0x8650a1c

pd = 0x8650a10

pa仍然是二義性的

另外注意f4函式,這時d類自定義的虛函式,那麼它放在什麼地方呢?應該放在第一虛函式表裡,也就是b::a::vfptr指向的虛函式表中

3. 虛繼承的記憶體布局

struct a

;struct b:virtual public a

;struct c:virtual public a

struct d

如果是虛繼承,那麼在d中只有乙個虛基類a的物件,這時就不會出現二義性的情況了

先看b的記憶體布局,由於b是虛繼承a,因此b中會有乙個指向虛表的指標,虛表裡面儲存有當前位置到虛基類的偏移和到當前類的開始位置處的偏移,例如b的記憶體布局

b::vptr

int b::b

int a::a

可以看到,a的位置放在了最底部,也就是記憶體位址最高的地方。vptr指向乙個虛表,虛表中儲存到虛基類的偏移,即8和到當前類的開始處的偏移,這裡是0

因此

sizeof(a)= 4;

sizeof(b) = 12;

如果有

pb = new b;

pa = pb;

我們輸出pa和pb的位址就可以看到

pa = 0x94a5a18

pb = 0x94a5a10

pa指向b的最底部,而不是最頂部

現在看d的記憶體布局

b::vptr

int b::b

c::vptr

int c::c

int d::d

int a::a

可以看到只有乙個a的物件處於d的最低部,那麼

sizeof(d) = 24

4. 帶有虛函式的虛繼承的記憶體布局

struct a

};struct b: virtual public a

};struct c: virtual public a

};struct d: public b, public c

};

如果同時具有虛函式和虛基類呢,這裡注意區分指向虛函式表的指標和指向虛表的指標

首先我們看

sizeof(a) = 8
a的布局為

a::vfptr

int a::a

再看b

sizeof(b) = 16
則b的記憶體布局為

b::vptr

int b::b

vfptr

int a::a

這裡的vfptr指向的虛函式表繼承自a,並且會對其中的字段進行修改(如果b重寫了虛函式或者自定了的新的虛函式)。例如我們分別輸出pa,pb,&pb->a以及&pb->b的值

pa = 0x8a08a18

pb = 0x8a08a10

&pb->a = 0x8a08a1c

&pb->b = 0x8a08a14

我們看到pa指向vftpr,也就是虛函式表

到這裡我們看d的記憶體布局

b::vptr

int b::b

c::vptr

int c::c

int d

vfptr

int a::a

這裡也只有乙個指向虛函式表的指標,於是

sizeof(d) = 28

指標對應的值為

pa = 0x8a92a24

pb = 0x8a92a10

pc = 0x8a92a18

pd = 0x8a92a10

&pd->a = 0x8a92a28

5. 虛表的作用

虛表裡面儲存了當前位置到虛基類的偏移和到當前類開始處的偏移,通過偏移就可以完成指標轉換

例如首先,找到pb對應的虛表,查詢

多重繼承 虛繼承與虛基類

一 多重繼承 單重繼承 乙個派生類最多只能有乙個基類 多重繼承 乙個派生類可以有多個基類 class 類名 繼承方式 基類1,繼承方式 基類2,派生類同時繼承多個基類的成員,更好的軟體重用 可能會有大量的二義性,多個基類中可能包含同名變數或函式 多重繼承中解決訪問歧義的方法 基類名 資料成員名 或成...

虛繼承和虛基類

虛繼承主要解決在多重繼承中的菱形繼承問題,也就是說 b和c類同時繼承了a類,然後d類繼承了b,c類,那麼d類的虛表就會有重複的函式指標。include using namespace std 虛基類 class person person person string name name name e...

虛基類和虛繼承

這算得上我個人的乙個總結上,其實也算不上什麼原創,只是為了過濾一下前輩的經驗,加深一下自己的記憶 虛繼承的目的是讓某個類做出宣告,承諾願意共享它的基類,被共享的那個基類就是虛基類 includeusing namespace std class a protected int a class b p...