c 的類的記憶體布局

2021-07-23 11:49:56 字數 4544 閱讀 6764

· c++的類的記憶體布局有如下規則

:1. nonstatic data member 

存放在class object中;

2. static data member, static/nonstatic member function存放在

class object之外.

// 所有例項執行於32

位機器上測試/

· 規則1和2:

class a

;assert(sizeof(a) == 4);

那麼在每乙個a

物件內,

只含有乙個

a,也就是

,sizeof(a) == 4 .

而b,foo(),bar(),

都不在class object之內,

他們在記憶體中有唯一實體.· 

3. 若類有

virtural function, 

則在class object 

中增加virtual pointer(vptr)

指向virtural function tabel(vtbl). vptr

在類中的位置有兩種情況

:1是在所有成員變數之後

,這麼做的好處是這個類能和

c語言相容

.2是在最前面

,這樣做的話

,虛擬繼承等實現會方便一點

,但是不能和c相容

.在前或後依賴編譯器實現.· 

規則3class b

;assert(sizeof(b) == 8);

乙個int

和乙個vptr,共8

位4. nonstatic data member 

若在同乙個

access section

中, 則變數在記憶體中的順序保證和宣告中的順序一致

(較晚出現的變數有較高的位址

).而不同

access section

中的資料順序則沒***

,依賴編譯器實現.· 

規則4,

沒啥好說的

,一般就算在不同的

access section,

都會按照一致的順序來宣告

.但是要注意順序一致不代表連續

.因為變數間可能會有一些

bytes

用於記憶體對齊

.5. 

記憶體對齊

: 類的各個成員

,第乙個成員位於

offset

為0的位置

,以後每個資料成員的偏移量必須是

min(#pragma pack(), 

這個資料成員自身的長度

)的倍數

。資料成員自身對齊後, 

類本身也要進行對齊

, 對齊將按照

min(#pragma pack(), 

類中長度最大的成員

)的倍數進行.· 

規則5class c

;assert(sizeof(c) == 8);

規則5有兩條子規則

,第一條對這個例子沒用

,經過第一條後

c的大小還是

5,可是第二條要求整個類要對齊

,那麼必須在

char b

後增加3bytes.

如果int

和char

的宣告順序反一下

,那麼為滿足第一條規則

,類已經需要對齊成8了

,已經是

8那麼第二條也滿足了.· 

6. 如果類為空

,編譯器會安插

1byte

的資料到類中

,以確保類的每個例項都會有唯一的記憶體位址

· 規則6

class d

{};assert(sizeof(d) == 1);

這1byte

是編譯器插進去的

,如果不插的話

,連續宣告

d a,b;

再取他們的位址

,就會變成一樣的了

.就無法分辨哪個變數是哪個了

.不過要注意的是,

任何類繼承了

d,只要裡面有

vptr

或者任何乙個變數

,那麼編譯器就不會在子類中加入這

1 byte了.(

這個是依賴於編譯器的

,而不是標準規定

.如果編譯器沒有去掉這

1byte的話,

那麼就要記憶體對齊了

.)· 

7. 繼承後

,子類的資料成員不會占用父類記憶體對齊用的空間

. c++

語言保證

:"出現在

derivd class

中的base class subobject

有其完整的原樣性"

· 規則7

· class c

;class e:public c

;assert(sizeof(e) == 16);

編譯器不會為了節省空間把e

的成員插入到

c為了記憶體對齊的而補出的空間中的

.這道題我面試的時候被問過,我答

16的時候面試官還認為錯了

,太浪費空間了

.但是這的確是唯一的正確解.· 

8. 如果類的繼承體系不是單一

,而是多重繼承

,但是不含虛擬繼承

,那麼有多少條繼承鏈

,記憶體布局中就有多少個

vptr.

多個繼承鏈的位置

,和繼承時的宣告順序一致.· 

規則8· 

class b

;class f

;class g:public b, public f

;assert(sizeof(g) == 20);

3個int,2

個vptr,

一共20

· 9. 

如果使用了虛擬繼承

,則先將

derived class

的不變部分布局

,然後再布局虛擬繼承的

base class,

而具體布局則有以下情況

:1. 

使用pointer strategy, 

每乙個虛擬繼承的類

,都有乙個額外的指標指向

base class

2. 使用

virtual table offset strategy, 

不加入額外的指標指向

base class,

而是在vtbl的-1

的offset

內放置該類與虛擬繼承的基類之間的

offset. 

這樣的話執行時則可以通過

derivedpointer + vptr[-1]得到.

3. 使用

virtual base class table. 

這個是微軟的做法

,不過書中並沒有具體描述怎麼做

,根據我的理解好像是在

vtbl

中加入乙個指標指向

base class.

如果採用2或

3, 那麼記憶體布局和

8並不會有太大區別,就是

virtual base class

跑到最後面去了.第

9種情況異常複雜

,建議看原書外加自己在多個編譯器上實踐實踐

.規則9

class h:virtual public b

;class i:virtual public b

;class j:public h, public i

;assert(sizeof(j) == 28);

4個int

一共是16,bhi3

個類都有各自的

vptr,16+4*3==28.

1.沒有虛函式的單繼承

1、成員變數根據其繼承和宣告順序依次放在後面

2、成員函式定義在類體之外

3、調整位元組對齊使

bus的

「運輸量

」達到最高效率

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

在沒有虛函式的情況下,多重繼承與單繼承沒有多大區別, 父類和父類的成員變數根據其繼承和宣告順序依次存放後面,子類的成員變數放在最末尾。

3.有虛函式的單繼承

1、虛函式表在類的最開始位置

2、子類覆蓋父類的虛函式在虛函式表得到體現

3、子類新增的虛函式新增到虛函式表的末尾

4.有虛函式的多繼承

1、在子類中的每乙個父類都有乙個虛函式表

2、子類新增的虛函式,按繼承順序放在第乙個父類的虛函式表中

3、子類覆蓋父類的虛函式在每乙個父類的虛函式表得到體現

4.有虛函式的重複多繼承

1、孩子類存在父類的兩份拷貝。

2、孩子類的第二個父類的虛函式表,有些虛函式條目直接跳轉到第乙個父類的虛函式表的同名函式的,這只是

vc的實現方式而已,其他編譯器可能不是這樣。

5.有虛函式的虛擬繼承

1、虛基類放在孩子類的末尾2、

vc實現虛基類的方式是每乙個子類用乙個指向虛基類表的指標

vbptr

,同時,子類的虛函式表不在包含父類的虛函式位址。

c 類的記憶體布局

本文基本上是對於stanley b.lippman的inside the c object model一書第一章第三章的概括,描述了c 類的記憶體布局情況.c 的類的記憶體布局有如下規則 1.nonstatic data member 存放在class object中 2.static data m...

c 類的記憶體布局

c 中的struct需要記憶體對齊,便於機器訪問該struct。每個物件 如果類含有虛函式 在首位址位置放置了vptr,指向自己的虛函式表。物件中不包含成員函式 靜態的或非靜態的 它們可以被物件共享,靜態成員函式沒有this指標,所以不能被物件呼叫,non static 成員函式隱含有乙個this指...

C 類幾種情況的記憶體布局

對於c 開發者來說,了解其記憶體是非常必要的,同時c 的多型 動態繫結 的原理也是很重要的。c 記憶體布局 需要了解的幾種情況 1 無虛函式,非繼承 class void fun2 private int a 記憶體布局 沒有虛函式,沒有繼承,所以只有成員變數需要分配空間。2 有虛函式,非繼承 cl...