先看一個空的類佔多少空間?
- 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字節對齊,的補齊規則。
具體的可以看我那篇《5分鐘搞定字節對齊》
至此,我們可以歸納以下幾個原則:
1.類的大小爲類的非靜態成員數據的類型大小之和,也就是說靜態成員數據不作考慮。
2.普通成員函數與sizeof無關。
3.虛函數由於要維護在虛函數表,所以要佔據一個指針大小,也就是4字節。
4.類的總大小也遵守類似class字節對齊的,調整規則。
上一篇文章研究了關於類大小的4條規則後,我們再結合虛函數表,來研究下類的大小。
- class Base
- {
- public:
- Base(){};
- virtual ~Base(){};
- void set_num(int num)
- {
- a=num;
- }
- virtual int get_num()
- {
- return a;
- }
- private:
- int a;
- char *p;
- };
- class Derive:public Base
- {
- public:
- Derive():Base(){};
- ~Derive(){};
- virtual int get_num()
- {
- return d;
- }
- private:
- static int st;
- int d;
- char *p;
- char c;
- };
- int main()
- {
- cout<<sizeof(Base)<<endl;
- cout<<sizeof(Derive)<<endl;
- return 0;
- }
在Base類裏添加了virtual int get_num()函數,而子類也重新實現了virtual int get_num()函數。
但是結果依然是
12
24
說明子類只是共用父類的虛函數表,因此一旦父類裏有虛函數,子類的虛函數將不計入sizeof大小。
這可以認爲是一個補充規則。