深入解析C++ Data Member內(nèi)存布局
如果一個類只定義了類名,沒定義任何方法和字段,如class A{};那么class A的每個實(shí)例占用1個字節(jié)的內(nèi)存,編譯器會會在這個其實(shí)例中安插一個char,以保證每個A實(shí)例在內(nèi)存中有唯一的地址,如A a,b;&a!=&b。如果一個直接或是間接的繼承(不是虛繼承)了多個類,如果這個類及其父類像A一樣沒有方法沒有字段,那么這個類的每個實(shí)例的大小都是1字節(jié),如果有虛繼承,那就不是1字節(jié)了,每虛繼承一個類,這個類的實(shí)例就會多一個指向被虛繼承父類的指針。還有一點(diǎn)值得說明的就是像A這樣的類,編譯器不一定會產(chǎn)生傳說中的那6個方法,這些方法只會在需要的時候產(chǎn)生,如class A沒有被任何地方使用那這些方法編譯器就沒有必要產(chǎn)生,如果這個類實(shí)例化了,那么會產(chǎn)生default constructor,而destructor則不一定產(chǎn)生。
如果一個類中有static data member,nonstatic data member,還有const data member,enum,那么它的內(nèi)存布局會是什么樣的呢,看下面簡單的類Point:
class Point
{
public:
Point():maxCount(10){}
private:
int X;
static int count;
int Y;
const int maxCount ;
enum{
minCount=2
};
};
Sizeof(Point)=12,為什么占12字節(jié)呢,我相信很多人都知道是哪幾個成員變量占用的,就是X,Y,maxCount,maxCount作為常量字段,但在Point的每個實(shí)例中可能有不同的值,當(dāng)然屬于Point實(shí)例的一部分,如果把maxCount定義成static,那它就不不是Point實(shí)例的一部分了,如果定義成static const int maxCount=1;則maxCount分配在.data段中,如果沒有初始化則分配在.bss段中,反正跟Point的實(shí)例無關(guān),count分配在.bss段中,minCount分配在.rdata段中,總之count,maxCount,minCount在編譯連接完成之后,內(nèi)存(虛擬地址)就分配好了,在程序加載的時候,會把他們的虛擬地址對應(yīng)上實(shí)際的物理地址。
Data member的內(nèi)存布局:nonstatic data member在class object中的順序和其申明的順序一樣,static data member和const member不在class object中因為他們只有一份,被class object共享,所以static data member和const data member,枚舉并不會響應(yīng)class object的大小。關(guān)于段的信息,我覺得是每個C/C++程序員必須知道的。而Point每次實(shí)例化的時候則只需要分配X,Y,maxCount需要的內(nèi)存。
每個類的data member在內(nèi)存中應(yīng)該是連續(xù)的,如果出現(xiàn)數(shù)據(jù)對齊的情況,可能中間會有空白地帶。請看下面幾個類:
class AA
{
protected:
int X;
char a;
};
class BB:public AA
{
protected:
char b;
};
class CC:public BB
{
protected:
char c;
};
Sizeof(AA)=8//對齊3字節(jié)
Sizeof(BB)=12//兩個3字節(jié)對齊
Sizeof(CC)=16//編譯器“無恥”的用了3個3字節(jié)對齊
編譯器為什么要無恥的在class CC中加3個3字節(jié)對齊呢,這樣每個CC的實(shí)例就大了9字節(jié)。如果編譯器不加這9字節(jié)的空白,那么CC的每個實(shí)例就是8字節(jié),前面的X占4字節(jié),后面的a,b,c占3字節(jié),加1字節(jié)的空白對齊,剛好8字節(jié),沒有誰很傻很天真的以為最好是占7字節(jié)吧。
如果CC占用8字節(jié)內(nèi)存,同樣的AA,BB都是8字節(jié)的內(nèi)存,這樣的話,如果把一個指向AA實(shí)例的指針賦給一個指向CC實(shí)例的指針,那么就會把AA中的8字節(jié)直接蓋到CC的8字節(jié)上,結(jié)果CC實(shí)例中的b,c都被賦上了不是我們想要的值,這很可能會導(dǎo)致你的程序出問題。
父類的data member會在子類的實(shí)例中有完整的一份,這樣在有繼承關(guān)系的類之間進(jìn)行類型轉(zhuǎn)換,就只用簡單的修改指針的指向。
Data Member的存取。對一個data member的存取,編譯器把對象實(shí)例的起始地址加上data member的偏移量。如CC c;
c.X=1;相當(dāng)于&c+(&CC::X-1),減一其實(shí)是為了區(qū)分是指向object的指針還是指向data member的指針,指向data member的要減一。每一個data member的偏移量在編譯的時候是知道的,根據(jù)成員變量的類型和內(nèi)存對齊,存在virtual繼承或是虛方法的情況編譯器會自動加上一些輔助的指針,如指向虛方法的指針,指向虛繼承父類的指針等。
在data member的存取效率上,struct member 、class member、單一繼承或是多重繼承的情況下效率都是一樣的,因為他們的存儲其實(shí)都是&obj+(&class.datamember-1)。在虛繼承的情況下,可能會影響存儲性能,如通過一個指針來存取一個指向虛繼承而來的data member,那么性能會有影響,因為在虛繼承的時候,在編譯的時候還不能確定這個data member是來自子類還是父類,只有在運(yùn)行的時候才能推斷出來,其實(shí)就是多了一步指針的操作,在虛繼承中,如果是通過對象實(shí)例來操作虛繼承而來的data member,則不會有任何性能問題,因為不存在什么多態(tài)性,所有東西在編譯的時候內(nèi)存地址都確定了。
虛繼承還是虛方法為了實(shí)現(xiàn)多態(tài)一樣,多了一步,如果不需要多態(tài),而是通過對象實(shí)例調(diào)用相關(guān)的方法就不會有性能問題。
上一篇:解析如何用指針實(shí)現(xiàn)整型數(shù)據(jù)的加法
欄 目:C語言
下一篇:C語言程序設(shè)計50例(經(jīng)典收藏)
本文標(biāo)題:深入解析C++ Data Member內(nèi)存布局
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/4343.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10深入理解約瑟夫環(huán)的數(shù)學(xué)優(yōu)化方法
- 01-10深入二叉樹兩個結(jié)點(diǎn)的最低共同父結(jié)點(diǎn)的詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計- 解析最少換車次數(shù)的問題詳解
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10深入第K大數(shù)問題以及算法概要的詳解
- 01-10深入解析最長公共子串
- 01-10c++中inline的用法分析


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達(dá)式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實(shí)例總結(jié)