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