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