首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 對象模型深度解析:佈局篇(類佈局、虛函數、繼承、菱形繼承)

     編輯:關於C++

前言:為什麼要懂對象模型?

C++ 將數據與函數封裝在類中,編譯器背後如何佈局內存?普通函數、成員函數、虛函數調用機制有何不同?static 成員又如何存放?搞清這些底層細節,能寫出更高效、更安全的代碼,也是面試必考硬核知識。

1. 類的整體內存佈局

非靜態數據成員 → 每個對象內含

class Point3d {
    float x, y, z;  // 直接內含於對象
public:
    void print() { /* 不佔用對象空間 */ }
};
// sizeof(Point3d) == 12 (3*float)

靜態數據成員 → 類級別,存放於全局數據區

class A { static int x; }; // sizeof(A) == 1 (空類佔1字節)

函數成員(非虛) → 類級別,不佔對象空間

虛函數 → 引入虛表指針 (vptr),每對象增大 sizeof(void*)

class C { virtual void foo(); }; // sizeof(C) == 8 (int + vptr)

2. 空類大小為 1 的原因

確保不同對象擁有唯一地址:&a != &b。編譯器隱式插入 1 byte char。

3. 內存對齊優化

class A { char x; int y; char z; };  // sizeof=12 (4字節對齊)
class B { char x; char y; int z; };  // sizeof=8  (優化後)
class D { long long x; char y; char z; }; // sizeof=16 (8字節對齊)

編譯器自動重排以減少填充,但 C++ 標準不保證成員聲明順序即內存順序(僅保證同訪問級別下順序一致)。

4. 虛函數與虛表機制

class X {
    int a, b;
    virtual void foo1() { cout << "X::foo1"; }
};
// 對象佈局:[vptr][a][b]
// vptr → 虛表:[&X::foo1]

驗證代碼:強制轉型 int** tmp = (int**)&obj,tmp[0][0] 即虛表地址,tmp[0][0][0] 為第一個虛函數地址。

5. 繼承下的佈局演進

單一繼承(無虛函數)

class A { int x; };
class B : public A { int y; }; // sizeof(B)=8, 佈局:[A::x][B::y]

基類含虛函數

class C { virtual void fooC(); };
class D : public C { virtual void fooD(); };
// D 佈局:[vptr][C::fooC][D::fooD] → 虛表合併,覆蓋同名槽位

多重繼承

class A { virtual void fooA(); };
class B { virtual void fooB(); };
class C : public A, public B { virtual void fooC(); };
// C 佈局:[A::vptr][A::a][B::vptr][B::b][C::c]
// 兩個虛表指針,分別指向 A、B 子物件虛表

菱形虛繼承

class A { int a1, a2; };
class B : virtual public A { int b; };
class C : virtual public A { int c; };
class D : public B, public C { int d; };
// D 佈局:[B::vptr][B::b][C::vptr][C::c][D::d][A::a1][A::a2]
// 虛基類 A 共享單份實例,放在最末尾,vptr 指向虛基類偏移表

6. 關鍵驗證技巧

通過 int** tmp = (int**)&obj 暴力解析對象內存:

  • tmp[0] → 第一個虛表指針
  • tmp[0][i] → 第 i 個虛函數地址
  • 多重繼承下 tmp[1]、tmp[2] 對應各基類子物件 vptr

總結

  1. 非靜態數據成員內含於對象,靜態成員/函數成員屬於類
  2. 虛函數引入 vptr + 虛表,單繼承合併虛表,多重繼承多 vptr
  3. 虛繼承將虛基類移至末尾,通過 vptr 間接訪問
  4. 內存對齊、空類佔 1 byte 是編譯器為唯一地址、訪問效率做的妥協

下一篇將深入「成員篇」:this 指針調整、靜態/非靜態成員訪問、虛函數調用開銷分析。

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