下面通過幾個例子大概展示了c++物件記憶體布局在複雜的繼承關係中使用的策略。
開始之前,先來讀讀下面這段話
c++ standard 並不強制規定如 「base class subobjects 的排列次序」 或 「不同訪問層級的 data很坑爹吧? >_<member 的排列次序」這種瑣碎細節。它也不規定 virtual functions 或 virtual base classes
的實現細節。c++ standard 只是說:那些細節由各家廠商自定。
也因此, **執行得到的具體數值與系統環境和編譯器有關,這對**沒有影響。
lz使用mingw-gcc
// ***** 弱爆了的分界線 *****//
先來看第乙個case
// case 1
class x {};
class y: public
virtual x {};
class z: public
virtual x {};
class a: public y, public z {};
// sizeof x y z a -> 1 4 4 8
對於乙個空的物件,編譯器會為它至少分配乙個byte。所以 sizeof(x) = 1。
虛繼承會造成相應的負擔,在 derived class 中,這種負擔反應在某種形式的指標上。gcc 的實現上,指標是指向虛表的。
指標是4位元組的,所以 y, z 的 size 至少是4位元組。
聰明的讀者一定會問,繼承來的基類子物件不是還有乙個 byte 嗎?
事實的確如此, 在一些機器上,sizeof(y) = 4(vptr) + 1(base subobject) + 3(alignment) = 8 byte
而像 gcc 這樣的編譯器, 為 empty virtual base class 提供特殊優化。
在這個策略下,乙個 empty virtual base class 被視為 derived class object 最開頭的一部分,所以 y, z 只包含乙個 vptr,size 就是 4 byte。也就是說它並沒有花費任何的空間, 這就節省了上面所說的編譯器所插入的 1 byte。
確定 a 的 size 時需要先解決乙個問題,x 在 a 中有幾個副本(儘管這裡可以優化)?
答案是, 只有 1 個
乙個 virtual base class subobject 只會 derived class 中存在乙份實體, 不管它在繼承體系中出現多少次!所以 sizeof(a) 的計算方式:
- 共享的唯一的 x 實體,優化後為 0
- base class y 的大小, 減去因 x 而配置的大小。base class z 同。4 - 0 + 4 - 0 = 8
- class a 自己的大小: 0
- class a 的對齊: 0
則 sizeof(a) = 8
case 2 和 case 3 的計算方式相同, 作為 excercise 吧。
// case 2
// 與 case 1 唯一的區別是 x 有了乙個 char 型別的 data member
class x ;
class y: public
virtual x {};
class z: public
virtual x {};
class a: public y, public z {};
// sizeof x y z a -> 1 8 8 12
// case 3
class x ;
class y: public
virtual x {};
class z: public
virtual x {};
class a: public y, public z {};
// sizeof x y z a -> 1 8 8 12
對於 case 4, 並沒有用到虛繼承,所以也不會有相應的負擔。
這種情況,可以簡單理解為在 derived class 中直接放入 base class。
// case 4
class
x ;class
y: public
x {};
class
z: public
x {};
class
a: public
y, public
z {};
// sizeof x y z a -> 1 1 1 2
further reading:
c++ 多繼承和虛繼承的記憶體布局 詳細解釋了由gcc編譯器實現多繼承和虛繼承的物件的布局
《深度探索C 物件模型》讀書筆記(5)
純虛函式 在設計抽象基類時,需要注意以下幾點 1 不要將destructor宣告為pure virtual function 如果將destructor宣告為pure virtual function,則設計者一定得定義它。因為每乙個derived class destructor會被編譯器加以擴充...
《深度探索C 物件模型》讀書筆記(6)
物件的構造和解構 一般而言,我們會把object盡可能放置在使用它的那個程式區段附近,這樣做可以節省不必要的物件產生操作和銷毀操作。全域性物件 全域性物件的靜態初始化策略包括以下幾個步驟 1 為每乙個需要靜態初始化的物件產生乙個 sti 函式,內含必要的constructor呼叫操作或inline ...
讀書筆記 《深度探索c 物件模型》 (3)
第四章 function語意學 4.2 虛擬成員函式 a 單一繼承下的virtual function 乙個class只會有乙個virtual table 這個class自己定義的virtual function,它override了乙個可能存在的base class virtual functio...