c 類的記憶體布局

2021-06-21 09:33:04 字數 2779 閱讀 2134

本文基本上是對於stanley b.lippman的inside the c++ object model一書第一章第三章的概括,描述了c++類的記憶體布局情況.

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

1. nonstatic data member 存放在class object中;

2. static data member, static/nonstatic member function存放在class object之外.

3. 若類有virtural function, 則在class object 中增加virtual pointer(vptr)指向virtural function tabel(vtbl). vptr在類中的位置有兩種情況:1是在所有成員變數之後,這麼做的好處是這個類能和c語言相容.2是在最前面,這樣做的話,虛擬繼承等實現會方便一點,但是不能和c相容.在前或後依賴編譯器實現.

4. nonstatic data member 若在同乙個access section中, 則變數在記憶體中的順序保證和宣告中的順序一致(較晚出現的變數有較高的位址).而不同access section中的資料順序則沒***,依賴編譯器實現.

5. 記憶體對齊: 類的各個成員,第乙個成員位於offset為0的位置,以後每個資料成員的偏移量必須是min(#pragma pack(), 這個資料成員自身的長度)的倍數

資料成員自身對齊後, 類本身也要進行對齊, 對齊將按照min(#pragma pack(), 類中長度最大的成員)的倍數進行.

6. 如果類為空,編譯器會安插1byte的資料到類中,以確保類的每個例項都會有唯一的記憶體位址

7. 繼承後,子類的資料成員不會占用父類記憶體對齊用的空間. c++語言保證:"出現在derivd class 中的base class subobject有其完整的原樣性"

8. 如果類的繼承體系不是單一,而是多重繼承,但是不含虛擬繼承,那麼有多少條繼承鏈,記憶體布局中就有多少個vptr.多個繼承鏈的位置,和繼承時的宣告順序一致.

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種情況異常複雜,建議看原書外加自己在多個編譯器上實踐實踐.

用例項來說說(32位機器)

規則1和2:

class a

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

那麼在每乙個a物件內,只含有乙個a,也就是,sizeof(a) == 4 .

而b,foo(),bar(),都不在class object之內,他們在記憶體中有唯一實體.

規則3class b

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

乙個int和乙個vptr,共8位

規則4,沒啥好說的,一般就算在不同的access section,都會按照一致的順序來宣告.但是要注意順序一致不代表連續.因為變數間可能會有一些bytes用於記憶體對齊.

規則5class c

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

規則5有兩條子規則,第一條對這個例子沒用,經過第一條後c的大小還是5,可是第二條要求整個類要對齊,那麼必須在char b後增加3bytes.

如果int和char的宣告順序反一下,那麼為滿足第一條規則,類已經需要對齊成8了,已經是8那麼第二條也滿足了.

規則6class d

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

這1byte是編譯器插進去的,如果不插的話,連續宣告d a,b;再取他們的位址,就會變成一樣的了.就無法分辨哪個變數是哪個了.

不過要注意的是,任何類繼承了d,只要裡面有vptr或者任何乙個變數,那麼編譯器就不會在子類中加入這1 byte了.(這個是依賴於編譯器的,而不是標準規定.如果編譯器沒有去掉這1byte的話,那麼就要記憶體對齊了.)

規則7class e:public c

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

編譯器不會為了節省空間把e的成員插入到c為了記憶體對齊的而補出的空間中的.這道題我面試的時候被問過,我答16的時候面試官還認為錯了,太浪費空間了.但是這的確是唯一的正確解.

規則8class f

;class g:public b, public f

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

3個int,2個vptr,一共20

規則9class 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.

這東西其實我一年半前就看書看到過,可惜實際程式設計中基本是用不到這種東西的,導致我忘了不少,面試的時候有幾個沒答出來,十分可惜,特意花一天時間重新啃了那書再總結總結加深記憶.另外真的要對自己說聲加油啊.

c 類的記憶體布局

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

c 的類的記憶體布局

c 的類的記憶體布局有如下規則 1.nonstatic data member 存放在class object中 2.static data member,static nonstatic member function存放在 class object之外.所有例項執行於32 位機器上測試 規則1和...

C 類繼承記憶體布局

c 類繼承記憶體布局 c 繼承分為兩種,普通繼承和虛擬繼承 virtual 具體的繼承又根據父類中的函式是否virtual而不同。下面就單繼承分為幾種情況闡述 1.普通繼承 父類無virtual函式 若子類沒有新定義virtual函式 此時子類的布局是 由低位址 高位址 為父類的元素 沒有vptr ...