首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

C++ 數據成員內存佈局深度解析:繼承、虛繼承、對齊、訪問效率

     編輯:更多程式設計知識

C++ 對象模型中,數據成員的內存佈局直接影響對象大小、繼承轉換、訪問性能。本文從空類、非靜態/靜態/常量成員、單繼承/多繼承/虛繼承、對齊填充、成員訪問機制六個維度深度剖析。

空類與最小對象大小


class Empty {};
// sizeof(Empty) == 1
// 原因:C++ 標準要求每個對象擁有唯一地址,編譯器注入 1 字節佔位符
Empty a, b;
assert(&a != &b);  // 地址必然不同

編譯器按需生成特殊成員函數(默認構造/析構/拷貝/移動/賦值),僅在被使用時才生成代碼(ODR-used)。

數據成員分類與佈局規則

成員類型存儲位置對象大小影響備註
非靜態數據成員對象內存內是按聲明順序佈局,受對齊影響
靜態數據成員全局數據段 (.data/.bss)否類共享單份實例,編譯期分配地址
常量數據成員對象內存內 (非 static const 整型)是每個對象可有不同值,構造函數初始化列表賦值
枚舉常量編譯期常量,不佔對象內存否本質是編譯期整數常量

實例:Point 類佈局分析


class Point {
public:
    Point() : maxCount(10) {}
private:
    int x;           // 偏移 0
    static int count; // 不在對象中
    int y;           // 偏移 4
    const int maxCount; // 偏移 8 (每個對象可異)
    enum { minCount = 2 }; // 不在對象中
};
// sizeof(Point) == 12 (x:4, y:4, maxCount:4)
// count 在 .bss 段,minCount 在 .rdata 段

繼承鏈佈局與對齊填充


class AA { protected: int x; char a; };        // sizeof=8 (x:4, a:1, padding:3)
class BB : public AA { protected: char b; };   // sizeof=12 (繼承8 + b:1 + padding:3)
class CC : public BB { protected: char c; };   // sizeof=16 (繼承12 + c:1 + padding:3)

關鍵現象:每層繼承獨立對齊,導致「階梯式填充」。

CC 內存佈局 (16 字節):
[AA::x:4][AA::a:1][pad:3][BB::b:1][pad:3][CC::c:1][pad:3]
 0-3     4     5-7    8      9-11   12      13-15

原因:若 CC 壓縮為 8 字節,基類子對象地址連續性被破壞,AA* p = new CC; p->x = 1; 寫入時會覆蓋 BB/CC 成員,導致未定義行為。編譯器犧牲空間換取基類子對象地址不變性,保證多態指針轉換安全。

虛繼承:共享基類子對象


class Base { int b; };
class Derived1 : virtual public Base { int d1; };
class Derived2 : virtual public Base { int d2; };
class Final : public Derived1, public Derived2 { int f; };

佈局特點:

  • Final 僅包含一份 Base::b
  • Derived1/2 包含 虛基類指針 (vbptr) 指向共享的 Base 子對象
  • Final 包含 vbptr 或 虛基類表指針 (vbtable pointer)
  • 對象大小顯著增加,但解決了「菱形繼承」數據冗余與二義性

數據成員訪問機制與效率

非虛繼承/單繼承:直接偏移量訪問


CC obj;
obj.x = 1;  // 編譯為:*(int*)(&obj + offsetof(CC, x)) = 1;
// offsetof 在編譯期已知,零開銷

效率等同於 struct、C 風格結構體、單繼承鏈。

虛繼承成員訪問:間接寻址


Final obj;
obj.b = 1;  // 編譯為:
// 1. 獲取 vbptr
// 2. 查 vbtable 找到 Base 偏移
// 3. 計算 Base 地址
// 4. 寫入 b
// 多一次間接寻址,但僅虛基類成員受影響

關鍵區別:

  • 通過對象訪問虛基類成員:編譯期可解析偏移,無性能損失
  • 通過指針/引用訪問虛基類成員:動態類型未知,需運行期查 vbtable

虛函數表指針 (vptr) 佈局

  • 含虛函數的類,對象首部/尾部包含 vptr (通常 8 字節)
  • 單繼承:衍生類共享基類 vptr,指向衍生類 vtable
  • 多繼承:每個帶虛函數的基類子對象有各自 vptr
  • 虛繼承:vptr 可能與 vbptr 合併或分離

總結:C++ 對象模型核心原則

  1. 非靜態數據成員按聲明順序連續佈局,受對齊插入填充
  2. 靜態/枚舉/常量(整型)不佔對象內存
  3. 繼承保證基類子對象地址不變,導致階梯式填充
  4. 虛繼承共享基類實例,引入 vbptr/vbtable 間接寻址
  5. 成員訪問編譯期解析偏移量,虛基類指針訪問除外
  6. 空類大小為 1,保證地址唯一性

理解佈局機制,是掌握 sizeof、對象切片、多態指針轉換、序列化佈局兼容、高性能內存設計的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved