本篇隨筆討論乙個比較冷門的知識,繼承結構中記憶體對齊的問題,如今記憶體越來越大也越來越便宜,大部分人都已經不再關注記憶體對齊的問題了。但是作為乙個有追求的技術人員,實現功能永遠都是最基本的要求,把**優化到自己想要的樣子才能從中找到真正的愉悅感。這便是我們追求細節的意義。
目錄引子-記憶體對齊示例與規則
高階-繼承體系中的記憶體對齊
引子-記憶體對齊示例與規則:
討論記憶體對齊,就要牽涉到#pragma pack(n)中定義n的大小。c語言中針對結構體提出了記憶體對齊的概念。下面請看**:
#include using執行結果:namespace
std;
#pragma pack(8)
struct
ethanol;
struct
ether;
intmain()
如果不清楚上面的資料如何產生,請對照下面內村對齊的規則:
x86(linux 預設#pragma pack(4), window 預設#pragma pack(8))。linux 最大支援 4
位元組對齊。
1 )取 pack(n)的值(n= 1 2 4 8--),取結構體中型別最大值 m。兩者取小即為外對齊大小 y= (m。
2 )將每乙個結構體的成員大小與 y 比較取小者為 x,作為內對齊大小.
3 )所謂按 x 對齊,即為位址(設起始位址為 0)能被 x 整除的地方開始存放資料。
4 )外部對齊原則是依據 y 的值(y 的最小整數倍),進行補空操作。
原則:先內後外。
結構體內元素不同的排列組合方式大概可以用化學中的同分異構體來比喻,比如乙醇和甲醚,他們有著完全相同的成分,但是化學性質卻不同。所以上面我用了ethanol和ether兩個名字來命名結構體。
過渡到c++中,類與結構提不僅可以通過組合的方式構成新型別,還可以通過繼承的方式來實現**的重用
由結構體延伸到類看看包含關係中的類大小:
#include using執行結果:namespace
std;
#pragma pack(8)
class
p1
virtual
void
printp1()
protected
:
intp1;
};class
sonprivate
:
intson;
p1 p1;
};int
main()
以上執行結果,如果對c++了解,那麼應該知道含有虛函式的類,除了成員變數大小外,虛函式表指標排在最前面。所以虛函式表指標的大小也要算進去。對照上面的記憶體對齊規則,我們計算一下上面的結果是怎麼來的:
#pragma pack(n) ,n = 8。系統和編譯器均為為64位,所以指標的大小是8byte。
1)取p1中的最大型別大小m(虛函式表指標大小)與n作比較,m==n,所以外對齊y==8。
2)結構體內每乙個元素與y做比較,取小者做內對其。所以,排列為下圖:
所謂對齊,就是以0為起始位址,對元素進行排列,使用乙個來能夠整除內對齊的位址來安放後乙個資料元素。
內對齊8+4=12
3)但是12不是外對齊的倍數,要用外對齊的最小整數倍來補齊8x2=16正好是這個類的大小。
高階-繼承體系中的記憶體對齊
繼承現象:
上面用足組合的關係來計算乙個類的大小是符合記憶體對齊規則的,這種包含關係與記憶體結構體中的對齊沒有任何區別,那如果改為繼承關係呢?
#include using執行結果:namespace
std;
#pragma pack(8)
class
p1
virtual
void
printp1()
protected
:
intp1;
};class son:public
p1private
:
intson;
//p1 p1;
};int
main()
彷彿有4個位元組丟失了。。。。。。
檢視記憶體排列:
#include usingnamespace
std;
#pragma pack(8)
class
p1
virtual
void
printp1()
protected
:
intp1;
};class son:public
p1private: int son; //
p1 p1;
};
intmain()執行結果:
由此結果我們得到的結論是派生類繼承並非完全繼承了基類的大小,卻繼承了基類未作外對齊的大小,由此執行結果我們可以看出派生類(son)類只繼承了基類(p1)12個位元組。對此感到疑惑的朋友可以繼續使用其它示例來檢驗這個結果。
由此我們給出以下總結
繼承體系中記憶體對齊的實質:
所謂繼承關係實質上是派生類繼承了基類中的元素(虛函式表和成員變數),而非繼承已經記憶體對齊固化的基類結構,基類中的元素被繼承到派生類中與派生類中新新增的元素需要重新按著元素的排列組合記憶體對齊。所以就有了上面包含關係與繼承關係完全不一樣的大小的現象。
C 繼承體系中的記憶體分段
綜述與目錄 討論這個問題之前我們先明確類的結構,乙個類的大概組成,下面的很多分類名詞都是我個人杜撰,為的就是讓讀者看懂能夠區分,下面分別分類 目錄 空類不含任何成員變數,也不繼承某個基類。結構類像c語言中結構體一樣,要麼只包含基本資料型別,要麼是其他構造型別的巢狀,或者兩者兼而有之。派生類有至少乙個...
C 中的記憶體對齊
記憶體對齊 在我們的程式中,資料結構還有變數等等都需要占有記憶體,在很多系統中,它都要求記憶體分配的時候要對齊,這樣做的好處就是可以提高訪問記憶體的速度。我們還是先來看一段簡單的程式 程式一 1 include 2using namespace std 34 structx15 1011struct...
C 中的記憶體對齊
在我們的程式中,資料結構還有變數等等都需要占有記憶體,在很多系統中,它都要求記憶體分配的時候要對齊,這樣做的好處就是可以提高訪問記憶體的速度。我們還是先來看一段簡單的程式 程式一 1 include iostream 2using namespace std 34 structx15 1011str...