C 記憶體分布之菱形繼承 無虛函式

2021-07-14 14:41:43 字數 3906 閱讀 1365

一、繼承關係圖

圖1.菱形類圖

二、源**

#includeusing namespace std;

class ancestor

int* get_c(void)

};class father:virtual public ancestor

int* fa_get_anc_c(void)

int* get_cc(void)

};class mother:virtual public ancestor

int* ma_get_anc_c(void)

int* get_ccc(void)

};class son :public father,public mother

};int main()

我們知道每乙個區域性變數一般都會儲存到棧中,如果想深入了解的話,同學們可以檢視linux中的區域性變數和棧。這裡在main函式中生成的都是區域性變數,所以為此我們可以根據彙編**,列出物件的分布圖,如下所示。

圖 3 main函式中臨時變數分布

當執行到int a = baba.c處,到底呼叫的是子類例項son中的父類資料域的c,還是共有的資料c。為了更清楚方便大家推算,我打出了執行這行**前後的暫存器列表資訊。

在執行int a = baba.c之前的暫存器狀態

(gdb) info registers all

rax 0x603080 6303872

rbx 0x603070 6303856

rcx 0x10 16

rdx 0x603070 6303856

rsi 0x4013b0 4199344

rdi 0x603080 6303872

rbp 0x7fffffffde60 0x7fffffffde60

rsp 0x7fffffffde10 0x7fffffffde10

..............................

int a =baba.c;

400af7: 48 8b 55 b8 mov -0x48(%rbp),%rdx "rdx=0x603070

400afb: 48 8b 45 b8 mov -0x48(%rbp),%rax "rdx=0x603070

400aff: 48 8b 00 mov (%rax),%rax "取出rax中的內容rax=0x401358 --------<1>

400b02: 48 83 e8 18 sub $0x18,%rax "將暫存器rax-0x18,即rax=0x401340

400b06: 48 8b 00 mov (%rax),%rax "取出0x401340的內容,rax=36 -------<2>

400b09: 48 8d 04 02 lea (%rdx,%rax,1),%rax "rax=0x603094 ---------<3>

400b0d: 8b 00 mov (%rax),%eax "0x603094就是c的位址

400b0f: 89 45 ec mov %eax,-0x14(%rbp) "將c的值放到rbp-0x14偏移處。

...........................

執行int a = baba.c之後的暫存器值列表

(gdb) info registers all

rax 0x58 88 "返回值就是88了

rbx 0x603070 6303856

rcx 0x10 16

rdx 0x603070 6303856

rsi 0x4013b0 4199344

rdi 0x603080 6303872

rbp 0x7fffffffde60 0x7fffffffde60

rsp 0x7fffffffde10 0x7fffffffde10

擴充套件:

<1>(gdb) p *0x603070 $1

= 4199256 = 0x401358

(gdb) p *er $2

= , _vptr.father = 0x401358,    

cc = 99}, = , d = 2, e = 5}

<2>(gdb) p *0x401340 $3

= 36

<3>lea    (%rdx,%rax,1),%rax 這句話的意思就是(rdx)+(rax)*1 即 =

0x603070

+36x1 = 0x603094 ,這裡就得出了c的位址。

針對上面的36我們再來看看son的vtable

vtable for son

son::_ztv3son: 6u entries

036u     //這個就是,編譯器在虛表中記下了ancestor資料域的偏移

8     (int (*)(...))0              

16    (int (*)(...))(& _zti3son)   

24    20u

32    (int (*)(...))-0x00000000000000010  

40    (int (*)(...))(& _zti3son)

總結:到現在我們就知道上面一開始丟擲的幾個問題了吧。

1.為什麼ancestor的c資料域,沒有放在子類物件首位址。

答:子類物件的首位址存放的是子類的虛表指標。由上面的除錯和log列印我們已經發現,在菱形繼承中,公共父類的資料域都是公用乙份的,並且都是放在子類最下面的資料區。如果我們把子類物件位址賦給父類指標,例如:father *fa;fa=&er;.這裡我們可以在上面的除錯中發現,編譯器會自動將對應father類的那部分資料區的首位址賦給fa。最後訪問c域時,即fa->c,也是訪問子類0x603094處的共有c資料。

2.為什麼子類物件son資料大小不是father和mother物件的之和加son自己的資料域(sizeof(son)=sizeof(father)+sizeof(mother)+son_data)。

答:由於子類物件資料區多出了mother虛表指標,所以大小不一樣。

3.為什麼將son物件強制轉化成father和mother物件他們的c資料域位址是同乙個位址,難道他們沒有繼承ancestor嗎?

答:上面一開始我看到子類物件中的father和mother資料域是8位元組對齊的,我以為只是續表指標是8位元組對齊,所以我將子類物件中對應father資料按4位元組位址列印,結果本應該是c資料域的地方列印的是0,所以子類屬於father的資料區中沒有c資料域

C 記憶體分布之單繼承和多繼承 無虛函式

現在來介紹一下我的環境 1.系統 ubuntu 12.04.2 lts 2.編譯器 gcc version 4.4.7 ubuntu linaro 4.4.7 1ubuntu2 3.偵錯程式 gnu gdb ubuntu linaro 7.4 2012.04 0ubuntu2.1 7.4 2012....

C 之菱形繼承與虛繼承 含虛函式

物件導向的三大特徵 封裝,多型,繼承 前面我們已經講了繼承的一些知識點,在這基礎上,我們講的時候再涉獵一些多型的只是。下面我們先接著上次講有虛函式的菱形虛繼承 首先什麼是虛函式。虛函式 在類裡面,函式前面有virtual關鍵字的成員函式就是虛函式。塊 class base base virtual ...

C 菱形繼承與虛繼承

12 29 study.cpp 定義控制台應用程式的入口點。include stdafx.h include using namespace std 菱形繼承 多繼承 兩個類共同繼承於同乙個類 class animal class sheep1 public animal class tuo1 pu...