本文將探討C++類中成員變量的內存分布情況,包括普通成員、靜態成員、虛函數等不同情況下的內存布局。
一、基本成員內存布局
1. 普通成員變量
普通成員變量按照聲明順序在內存中連續排列(受訪問修飾符和內存對齊影響):
class NormalClass {
public:int a; // 4字節char b; // 4字節(考慮int對齊)double c; // 8字節char d; // 8字節(考慮double 對齊)
};
// sizeof(NormalClass) = 24 (考慮對齊填充)
內存布局:
| 0-3: int a | 4: char b | 5-7: padding | 8-15: double c | 16: char d | 17-23: padding |
2. 帶繼承的內存布局
派生類的成員追加在基類成員之后:
class Base {
public:int base_var; // 4字節
};class Derived : public Base {
public:int derived_var; // 4字節,再加上Base 類的 4字節,總共8字節
};
內存布局:
| 0-3: Base::base_var | 4-7: Derived::derived_var |
二、靜態成員的內存分布
靜態成員不占用類實例的內存空間,存儲在全局數據區
:
class WithStatic {
public:int normal_var; // 占用實例空間static int static_var; // 不占用實例空間
};
// sizeof(WithStatic) == sizeof(int)
三、虛函數對內存的影響
1. 含有虛函數的類
編譯器會隱式添加虛表指針(vptr),通常放在對象起始位置,虛函數表(vtable)本身不占用類實例的內存空間,vtable 存儲在程序的只讀數據段(全局靜態區)
,每個類(而非對象)共享一個 vtable。虛表指針(vptr)會占用類實例的內存空間,每個對象實例中存儲一個指向 vtable 的指針,在 64 位系統中占用 8 字節,32 位系統中占用 4 字節:
class WithVirtual {
public:virtual void foo() {} // 添加vptrint a;
};
// 在32位系統上sizeof(WithVirtual) == 8 (vptr + int)
// 在64位系統上sizeof(WithVirtual) == 16 (vptr + int + padding)
內存布局(64位系統):
| 0-7: vptr | 8-11: int a | 12-15: padding |
2. 繼承體系中的虛函數
派生類與基類共享同一個vptr(單繼承情況下):
class BaseWithVirtual {
public:virtual void foo() {}int base_var;
};class DerivedVirtual : public BaseWithVirtual {
public:virtual void bar() {} // 添加到虛表int derived_var;
};
內存布局(64位系統):
| 0-7: vptr | 8-11: Base::base_var | 12-15: Derived::derived_var |
四、多繼承的內存布局
多繼承情況下,每個基類子對象按聲明順序排列:
class Base1 { public: int base1_var; };
class Base2 { public: int base2_var; };class MultiDerived : public Base1, public Base2 {
public:int derived_var;
};
內存布局:
| 0-3: Base1::base1_var | 4-7: Base2::base2_var | 8-11: derived_var |
五、驗證內存布局的代碼
#include <iostream>
#include <cstddef>#define PRINT_OFFSET(className, member) \std::cout << "Offset of " #member ": " \<< offsetof(className, member) << std::endlstruct Test {virtual void foo() {} // 添加vptrint a;char b;static int c;
};int main() {std::cout << "Sizeof Test: " << sizeof(Test) << std::endl;PRINT_OFFSET(Test, a); // 64位系統輸出 8PRINT_OFFSET(Test, b); // 64位系統輸出 12return 0;
}
總結表格
特性 | 是否影響實例大小 | 存儲位置 |
---|---|---|
普通成員變量 | 是 | 棧/堆 |
靜態成員變量 | 否 | 全局數據區 |
虛函數 | 是(添加vptr) | 虛表 |
繼承 | 是 | 追加基類成員 |
虛繼承 | 是 | 更復雜布局 |
注意事項:
- 實際內存布局可能因編譯器實現不同而有所差異
- 使用
offsetof
宏驗證偏移量- 內存對齊可通過
#pragma pack
指令調整