總結:空類佔用1個字節,非空類的大小=非靜態成員變量佔用大小(靜態變量不佔內存)+虛函數佔用內存(4字節,不管有多少個虛函數)+字節對齊的開銷(成員函數不算入類的空間),子類大小=本身成員變量大小+父類大小
來看一個總的例子:
#include<stdio.h>
class Car
{
public:
Car(void){};
~Car(void){};
public:
int a;
char b;
char* c;
static int d;
void Fun(){};
void Fun1()
{
int a1;
long a2;
};
virtual Fun3(){};
virtual Fun4(){};
};
void main()
{
int size = 0;
Car objCar;
size = sizeof(objCar);
printf("%s %d /r", "Class Car Size:", size);
}
輸出結果:16
其中int a佔4個字節,char b佔一個字節,char * c佔4個字節,static int d不佔空間,void Fun成員函數不佔空間,void Fun1裏面定義了兩個變量也不佔空間,Fun3跟Fun4只佔4個字節(指針所佔空間,具體見下文分析)。
4+1+3(字節對齊)+4+4=16
以下引用:http://blog.csdn.net/yzx0803060320/article/details/7302130
先看這麼個問題——已知:
{
int a;
char *p;
};
那麼運行cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl;之後輸出什麼?
這個應該很簡單,兩個成員變量所佔的大小有嘛——8。可由時候人就是愛犯這個錯誤:這麼簡單的問題人家會問你?再想想……好像C++類裏面有個什麼 函數指針,也應該佔字節吧!?什麼指針來着?忘了(還是水平低不紮實)!流汗中……算了姑且認爲是構造函數和析構函數吧。一人一個加上剛纔那8個16個。 好笑嗎?這是我犯的錯誤!!!到底C++類的sizeof是多少呢?沒有所謂的函數指針問題嗎?不甘心,編個例子看看:
第一步:給丫來個空的(不好意思上火粗魯了)
{
};
運行cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl;
sizeof(CBase)=1;
爲什麼空的什麼都沒有是1呢?查資料……查啊查……OK這裏了:先了解一個概念:類的實例化,所謂類的實例化就是在內存中分配一塊地址,每個實例在內存中都有獨一無二的地址。同樣空類也會被實例化(別拿豆包不當乾糧,空類也是類啊),所以編譯器會給空類隱含的添加一個字節,這樣空類實例化之後就有了 獨一無二的地址了。所以空類的sizeof爲1。繼續下一步:
第二步:
還是最初的那個類,運行結果:sizeof(CBase)=8
沒什麼說的,兩個內部變量的大小。難道我記錯了沒有什麼指針問題的存在?再試試(早這麼有求知慾也不會丟人了,這回來勁了)
第三步:添個虛函數
{
public :
CBase( void );
virtual ~CBase(void );
private :
int a;
char *p;
};
再運行:sizeof(CBase)=12
嗨!問題出來了!!跟虛函數有關。爲什麼呢?查資料ing……
有了:“C++ 類中有虛函數的時候有一個指向虛函數的指針(vptr),在32位系統分配指針大小爲4字節”噢原來如此害死我了。那麼繼承類呢?
第四步:
基類就是上面的了不寫了
public CBase
{
public :
CChild( void );
~ CChild(void);
private :
int b;
};
運行:cout<<"sizeof(CChild)="<<sizeof(CChild)<<endl;
輸出:sizeof(CChild)=16;
可見子類的大小是本身成員變量的大小加上父類的大小。
關於虛擬繼承(相當於添加了一個接口):
class COneMember
{
public:
COneMember(int iValue = 0){m_iOne = iValue;};
private:
int m_iOne;
};class CTwoMember:virtual public COneMember
{
private:
int m_iTwo;
};
長度:12
內存結構:
E8 2F 42 00 //指針,指向一個關於偏移量的數組,且稱之虛基類偏移量表指針 CC CC CC CC // m_iTwo 00 00 00 00 // m_iOne(虛基類數據成員) |
關於閉合繼承:
class ClassA
{
public:
ClassA(int iValue=1){m_iA = iValue;};
private:
int m_iA;
};
class ClassB:public ClassA
{
public:
ClassB(int iValue=2){m_iB = iValue;};
private:
int m_iB;
};
class ClassC: public ClassC
{
public:
ClassC(int iValue=3){m_iC = iValue;};
private:
int m_iC;
};
class CComplex :public ClassB, public ClassC
{
public:
CComplex(int iValue=4){m_iComplex = iValue;};
private:
int m_iComplex;
};
長度:24
內存結構:
14 30 42 00 //ClassB的虛基類偏移量表指針 02 00 00 00 //m_iB C4 2F 42 00 //ClassC的虛基類偏移量表指針 03 00 00 00 //m_iC 04 00 00 00 //m_iComplex 01 00 00 00 //m_iA |
評註:和預料中的一樣,虛基類的成員m_iA 只出現了一次,而且是在最後邊。當然了,更復雜的情況要比這個難分析得多,但虛繼承不是我們研究的重點,我們只需要知道:虛繼承利用一個“虛基類偏移量表指針”來使得虛基類即使被重複繼承也只會出現一次。
看一下關於static成員
class CStaticNull { public: CStaticNull(){printf("Construct\n");} ~CStaticNull(){printf("Desctruct\n");} static void Foo(){printf("Foo\n");} static int m_iValue; }; |
長度:1
內存結構:(同CNull2 )
評註:可見static 成員不會佔用類的大小,static成員的存在區域爲靜態區,可認爲它們是“全局”的,只是不提供全局的訪問而已,這跟C
的static 其實沒什麼區別。
帶有虛函數的類:
class CVirtualNull
{
public:
CVirtualNull(){printf("Construct\n");}
~CVirtualNull(){printf("Desctruct\n");}
virtual void Foo(){printf("Foo\n");} //或者是virtual void Foo()=0;都是佔4個字節大小
};
長度:4
內存結構:
00 31 42 00 //指向虛函數表的指針(虛函數表後面簡稱“虛表”)
00423100:(虛表) 41 10 40 00 //指向虛函數Foo 的指針
00401041: E9 78 02 00 00 E9 C3 03… // 函數Foo 的內容(看不懂) |
子類有新的虛函數:
class CVirtualDerived: public CVirtualNull { public: CVirtualDerived(){m_iVD=0xFF;}; ~CVirtualDerived(){}; virtual void Foo2(){printf("Foo2\n");}; private: int m_iVD; }; |
長度:8
內存結構:
24 61 42 00 //虛表指針 FF 00 00 00 //m_iVD
00426124:(虛表) 23 10 40 00 50 10 40 00 |
評註:虛表還是隻有一張,不會因爲增加了新的虛函數而多出另一張來,新的虛函數的指針將添加在複製了的虛表的後面。
注意:當空類多繼承空類時,一個空類佔一個字節,一個空類繼承一個空類還是佔一個字節,一個空類繼承兩個空類時還佔一個字節;一個空類繼承N(N>2)個空類時,佔N-1個字節。不知道爲什麼,誰知道告訴我