1. 程式人生 > >C++類的大小——sizeof(class)

C++類的大小——sizeof(class)

第一:空類的大小

class CBase
{
};
執行cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl;

輸出

sizeof(CBase)=1;

為什麼空的什麼都沒有是1呢?

先了解一個概念:類的例項化,所謂類的例項化就是在記憶體中分配一塊地址,每個例項在記憶體中都有獨一無二的地址。同樣空類也會被例項化,所以編譯器會給空類隱含的新增一個位元組,這樣空類例項化之後就有了獨一無二的地址了。所以空類的sizeof為1。

第二:一般非空類大小

class CBase
{
    int  a;
    char *p;
};

執行結果:

sizeof(CBase)=8

第三:有虛擬函式類

class CBase
{
public:
    CBase(void);
    virtual ~CBase(void);
private:
    int   a;
    char *p;
};

執行結果:

sizeof(CBase)=12

“C++ 類中有虛擬函式的時候有一個指向虛擬函式的指標(vptr),在32位系統分配指標大小為4位元組”。

第四:有虛擬函式類的繼承

基類就是上面的了不寫了

class CChild :
    public CBase
{
public:
    CChild(void);
    ~CChild(void);
private:
    int b;
};

執行結果:

sizeof(CChild)=16;

可見子類的大小是本身成員變數的大小加上父類的大小。

另外:

1. 空類
class A
{
};
 
void main()
{
    printf("sizeof(A): %d\n", sizeof(A));
    getchar();
}

得到結果為:1。
類的例項化就是給每個例項在記憶體中分配一塊地址。空類被例項化時,會由編譯器隱含的新增一個位元組。所以空類的size為1。

2.虛擬函式
class A
{
    virtual void FuncA();
    virtual void FuncB(); 
};

得到結果:4
當C++ 類中有虛擬函式的時候,會有一個指向虛擬函式表的指標(vptr),在32位系統分配指標大小為4位元組。所以size為4.

3.靜態資料成員
class A
{
  int a;
  static int b;
  virtual void FuncA();
};

得到結果:8
靜態資料成員被編譯器放在程式的一個global data members中,它是類的一個數據成員.但是它不影響類的大小,不管這個類實際產生了多少例項,還是派生了多少新的類,靜態成員資料在類中永遠只有一個實體存在。

而類的非靜態資料成員只有被例項化的時候,他們才存在.但是類的靜態資料成員一旦被宣告,無論類是否被例項化,它都已存在.可以這麼說,類的靜態資料成員是一種特殊的全域性變數.
所以該類的size為:int a型4位元組加上虛擬函式表指標4位元組,等於8位元組。

4.普通成員函式
class A
{
    void FuncA();
}

結果:1
類的大小與它的建構函式、解構函式和其他成員函式無關,只已它的資料成員有關。

5.普通繼承
class A
{
    int a;
};
class B
{
  int b;
};
class C : public A, public B
{
  int c;
};

結果為:sizeof(C) =12.
可見普通的繼承,就是基類的大小,加上派生類自身成員的大小。

6.虛擬繼承
class C : virtual public A, virtual public B
{
  int c;
};

結果:16.

當存在虛擬繼承時,派生類中會有一個指向虛基類表的指標。所以其大小應為普通繼承的大小(12位元組),再加上虛基類表的指標大小(4個位元組),共16位元組。

類的大小問題實驗剖析:

1、空類:

C++編譯器強制給這種類插入一個預設成員,長度為1。如果有自定義的變數,變數將取代這個預設成員。

class A

{};

cout<<sizeof(A); //輸出1
2、只有一個char型
class A

{

char c;

};

cout<<sizeof(A); //輸出1
3、有5個char型
class A

{

char a,b,c,d,e;

};

cout<<sizeof(A); //輸出5
4、一個char型 + 一個int型:位元組對齊
class A
{
char c;

int a;

};

cout<<sizeof(A); //輸出8
5、2個char型 + 一個int型
class A

{

char c,d;

int a;

};

cout<<sizeof(A); //輸出8
6、5個char型 + 一個int型
class A

{

char c,d,e,f,g;

int a;

};

cout<<sizeof(A); //輸出12
7、1個char型 + 1個int型 + 2個char型
class A
{
char c;
int a;
char d,e;

};

cout<<sizeof(A); //輸出12
8、普通函式不佔空間
class A
{
void B(){ int d; }//0Byte
int C(){};

};

cout<<sizeof(A); //輸出1,等同於空類
9、虛擬函式 佔4個位元組:指向虛擬函式表的指標
class A
{
virtual void C(){}
};

cout<<sizeof(A); //輸出4
10、多個虛擬函式等同於1個虛擬函式
class A
{
virtual void C(){}

virtual void D(){}
};

cout<<sizeof(A); //輸出4
11、多繼承問題中sizeof
class b{};

class c :public b

{

virtual void fun() = 0;
};

class d :public b ,public c{};

cout<<sizeof(d); //輸出8,位元組對齊(b+c)
12、單繼承問題中sizeof
class b{};

class c :public b

{

virtual void fun() = 0;

};

class d :public c{};

cout<<sizeof(c); //輸出4

cout<<sizeof(d); //輸出4
13、靜態資料成員 和 成員函式 不佔空間
class A

{

static int a;

static int b(){};

};

cout<<sizeof(A); //輸出1
14、const資料變數佔空間 和 const成員函式不佔空間
class A

{

public:

const int b;

const int c;

virtual void f(){}

int d() const{}

int e() const{}

virtual void g(){}

A() :b(2), c(3)

{

}

};

cout<<sizeof(A); //輸出12= 4+4+4(多個虛擬函式算一個)

空類的大小

class Base
{
public:
    Base();
    ~Base();

};
  • 注意到我這裡顯示聲明瞭構造跟析構,但是sizeof(Base)的結果是1.

  • 因為一個空類也要例項化,所謂類的例項化就是在記憶體中分配一塊地址,每個例項在記憶體中都有獨一無二的地址。同樣空類也會被例項化,所以編譯器會給空類隱含的新增一個位元組,這樣空類例項化之後就有了獨一無二的地址了。所以空類的sizeof為1。

  • 而解構函式,跟建構函式這些成員函式,是跟sizeof無關的,也不難理解因為我們的sizeof是針對例項,而普通成員函式,是針對類體的,一個類的成員函式,多個例項也共用相同的函式指標,所以自然不能歸為例項的大小。

    接著看下面一段程式碼

class Base
{
public:
    Base();                
    virtual ~Base();         //每個例項都有虛擬函式表
    void set_num(int num)    //普通成員函式,為各例項公有,不歸入sizeof統計
    {
        a=num;
    }
private:
    int  a;                  //佔4位元組
    char *p;                 //4位元組指標
};

class Derive:public Base
{
public:
    Derive():Base(){};     
    ~Derive(){};
private:
    static int st;         //非例項獨佔
    int  d;                     //佔4位元組
    char *p;                    //4位元組指標

};

int main() 
{ 
    cout<<sizeof(Base)<<endl;
    cout<<sizeof(Derive)<<endl;
    return 0;
}

結果自然是

12

20
  • Base類裡的int  a;char *p;佔8個位元組。

  • 而虛解構函式virtual ~Base();的指標佔4子位元組。

  • -其他成員函式不歸入sizeof統計。

  • Derive類首先要具有Base類的部分,也就是佔12位元組。

  • int  d;char *p;佔8位元組

  • static int st;不歸入sizeof統計

  • 所以一共是20位元組。

 

在考慮在Derive里加一個成員char c;

class Derive:public Base
{
public:
    Derive():Base(){};
    ~Derive(){};
private:
    static int st;
    int  d;
    char *p;
    char c;

};

這個時候,結果就變成了

12

24

一個char c;增加了4位元組,說明類的大小也遵守類似class位元組對齊,的補齊規則。

至此,我們可以歸納以下幾個原則:

1.類的大小為類的非靜態成員資料的型別大小之和,也就是說靜態成員資料不作考慮。

2.普通成員函式與sizeof無關。

3.虛擬函式由於要維護在虛擬函式表,所以要佔據一個指標大小,也就是4位元組。

4.類的總大小也遵守類似class位元組對齊的,調整規則。

5、不佔空間的有:普通函式,靜態資料成員,靜態成員函式。

6、無論多少個,只相當於一個所佔的空間:虛擬函式。

7、空類佔1個位元組。

8、既有字元型又有整型,要考慮位元組對齊。

9、普通資料成員、const資料成員佔空間;靜態成員不佔空間。

參考:
https://blog.csdn.net/u010069101/article/details/51045840
https://blog.csdn.net/hairetz/article/details/4171769
https://blog.csdn.net/zzhongcy/article/details/38361755