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

C++ 內存佈局深度解析:虛函數表、繼承與虛繼承的記憶體模型

     編輯:關於C++

理解 C++ 對象在記憶體中的佈局,是掌握多態、繼承、虛繼承等核心機制的鑰匙。本文從單類虛函數表切入,層層遞進分析 9 種繼承場景的記憶體模型,配合圖解與 sizeof 實測,帶您窺探編譯器背後的佈局邏輯。

虛函數基礎:vptr 與 vtbl

  • 含虛函數的類,編譯器為每個類生成一張 虛函數表,表內存放虛函數地址。
  • 每個對象實例開頭隱含一個 vptr,指向其類的 vtbl。
  • 虛函數呼叫過程:obj->vptr → vtbl[slot] → function address。
  • 地址翻譯取決於對象實際記憶體地址,而非靜態類型,這就是動態綁定的本質。

場景實測與佈局圖解

測試環境:x86 Win32 Debug,指標 4 字節,double 8 字節。

2.1 單類含虛函數

template<typename T> class CPoint {
    T _x, _y, _z;
    virtual void setX(T); virtual void setY(T); virtual void setZ(T);
    virtual T getX() const; virtual T getY() const; virtual T getZ() const;
};
// sizeof(CPoint<double>) = 32 bytes
// 佈局:[vptr(4)] [_x(8)] [_y(8)] [_z(8)] + 4字節對齊填充 = 32

單類sizeof輸出

單類記憶體佈局

2.2 普通繼承,派生類無新虛函數

template<typename T> class CPoint2D : public CPoint<T> { /* 無新虛函數 */ };
// sizeof(CPoint2D<double>) = 32,佈局與基類完全一致
// vptr 指向基類 vtbl(派生類共用基類虛表)

繼承sizeof輸出

繼承記憶體佈局

2.3 普通繼承,派生類覆寫虛函數

class CPoint2D : public CPoint {
    virtual T getZ() const override { ... }
    virtual void setZ(T) override { ... }
};
// sizeof 仍為 32
// vptr 指向「派生類專屬 vtbl」:前 4 槽繼承基類,後 2 槽被覆寫函數取代

覆寫虛函數佈局

關鍵結論

  1. 虛函數表指針 永遠位於對象起始地址。
  2. 覆寫虛函數會導致派生類生成新的 vtbl,相應槽位替換為派生類函數地址。
  3. 非虛繼承下,單繼承對象佈局 = 基類子對象 + 派生類新增成員,無額外指標開銷。

虛繼承與多重繼承佈局(節選)

2.4 虛繼承,基類無虛函數

class Base { int nCount; };
class Derived : virtual public Base { int nDeriveCount, nCurrentNum; };
// sizeof(Derived) = 14 bytes (預期 12 + 2? 實測 14)
// 佈局:[vbptr(4)] [Base::nCount(4)] [Derived::nDeriveCount(4)] [Derived::nCurrentNum(4)]
// vbptr 指向虛基類表,用於運行期找到共享的虛基類子對象偏移

虛繼承sizeof輸出

虛繼承記憶體佈局

後續場景預告

本文僅展示前 4 種場景,剩餘 5 種(虛繼承+虛函數、多層虛繼承、多重繼承、菱形繼承等)佈局更為複雜,涉及 vbptr、vbtable、多 vptr、thunk 調整器 等機制,將在後續文章詳細拆解。

實用技巧:如何查看對象佈局

  • MSVC:/d1 reportSingleClassLayout<ClassName> 編譯選項
  • GCC/Clang:-fdump-class-hierarchy 或 -fdump-tree-original
  • 運行期:printf("%p\n", &obj) 配合風火輪調試器查看記憶體

深入理解記憶體佈局,不僅能解釋 sizeof、切片問題、指標轉換行為,更是高效能 C++ 開發(如內存池、序列化、零拷貝)的基石。

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