C++ 高頻面試:類的大小由什么因素決定?
在 C++中,類(或結(jié)構(gòu)體)的大小主要由以下幾個因素決定:
1. 非靜態(tài)成員變量
類的大小首先由其非靜態(tài)數(shù)據(jù)成員的總大小決定。
成員變量的排列順序和類型直接影響內(nèi)存布局(可能因?qū)R填充而增大)。
靜態(tài)成員變量(static)不占用類實例的大小,因為它們存儲在全局/靜態(tài)存儲區(qū)。
2. 內(nèi)存對齊(Alignment Padding)
為了滿足硬件對齊要求,編譯器可能在成員之間或末尾插入填充字節(jié)(Padding)。
對齊規(guī)則包括:
- 每個成員的對齊要求:由成員自身的類型決定(例如 int 需要 4 字節(jié)對齊,double 需要 8 字節(jié)對齊)。
- 結(jié)構(gòu)體的總對齊值:由所有成員中最大的對齊值決定(假如有 int 和 double,則是 double 的 8 字節(jié)對齊)。
- 總大小必須是結(jié)構(gòu)體對齊值的整數(shù)倍(即 8 的倍數(shù))。
例如:
struct A
{
double a;
int b;
int c;
}
sizeof(A) = 16
大部分編譯器輸出都是 16。
把順序換一下:
struct A
{
int b;
double a;
int c;
}
sizeof(A) = 24
大部分編譯器輸出是 24。
可通過 alignas 手動指定對齊方式,或使用 #pragma pack 修改對齊規(guī)則(網(wǎng)絡(luò)通訊中經(jīng)常使用1字節(jié)對齊,為了節(jié)省內(nèi)存還有流量帶寬)
3. 虛函數(shù)(動態(tài)多態(tài))
如果類包含虛函數(shù),編譯器會為其添加一個虛函數(shù)表指針(vptr),通常占用 sizeof(void*)(32 位系統(tǒng)為 4 字節(jié),64 位為 8 字節(jié))。
虛繼承(virtual 繼承)可能引入額外的開銷(如虛基類指針)。
4. 繼承關(guān)系
派生類的大小包括基類的所有非靜態(tài)成員(可能因繼承方式不同而調(diào)整)。
多重繼承一般會導(dǎo)致多個虛表指針(每個基類獨立)。
5. 空類的大小
空類(無成員、無虛函數(shù))的大小通常為 1 字節(jié),用于確保不同實例有唯一地址。
如果作為基類,可能被優(yōu)化為 0 字節(jié)(空基類優(yōu)化,EBCO)。
6. 編譯器優(yōu)化
某些編譯器可能合并填充區(qū)域或優(yōu)化布局(如 -O3 下的優(yōu)化)。
C++11 的 final 或 override 等關(guān)鍵字不影響大小。
示例代碼:
#include <iostream>
usingnamespace std;
class A {}; // 空類,大小=1
class B {
int x; // 4字節(jié)
char c; // 1字節(jié)
// 填充3字節(jié)(假設(shè)默認(rèn)對齊為4)
}; // 大小=8
class C {
virtualvoidfoo(){} // 虛函數(shù)指針:8字節(jié)(64位系統(tǒng))
int x; // 4字節(jié)
// 填充4字節(jié)(對齊到8)
}; // 大小=16
int main(){
cout << "A: " << sizeof(A) << endl; // 1
cout << "B: " << sizeof(B) << endl; // 8
cout << "C: " << sizeof(C) << endl; // 16
return0;
}
總結(jié)
- 成員變量是主要因素,靜態(tài)成員不計算在內(nèi)。
- 對齊規(guī)則可能導(dǎo)致額外填充。
- 虛函數(shù)引入虛表指針開銷。
- 繼承可能增加基類成員和額外信息。
- 空類最小為 1 字節(jié)。
- 可以通過 sizeof 運算符驗證類的大小,或使用 offsetof 宏檢查成員偏移量。