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

C++ 對象模型深度解析:內存佈局、虛表、繼承與多態實現

     編輯:關於C語言

C++ 對象模型兩大核心

  1. 語言級支援:構造/析構、虛函數、繼承 (單/多/虛)、多態
  2. 底層實現機制:數據佈局、虛表指針、RTTI、成員訪問、多態分發

三種對象模型演進

模型特點缺點
簡單對象模型對象存指針表,成員統一間接訪問空間浪費、訪問慢、對象大小隨成員數變化
表格驅動模型數據表 + 函數表,對象存兩表指針雙重間接、空間固定但訪問開銷大
C++ 實際模型非靜態數據成員內嵌對象,其餘外置;虛函數表 + vptr 支援多態非靜態數據成員變更需重編譯

C++ 對象模型核心結構

C++ 對象模型內存佈局

  • 非靜態數據成員:直接內嵌對象內存中
  • 靜態數據成員 / 所有函數成員:存儲於對象外部 (類作用域)
  • 虛函數機制:
    1. 每類生成虛表 (vtbl):存虛函數地址
    2. 每對象含 vptr:指向其類虛表
    3. vptr 初始化/重置由構造/析構/拷貝賦值自動完成
  • RTTI:虛表前存 type_info 指針,支援 dynamic_cast/typeid

驗證代碼:探測對象內存佈局

class Base {
public:
    Base(int v) : iBase(v) {}
    virtual ~Base() {}
    virtual void print() const { cout << "Base::print" << endl; }
    int getIBase() const { return iBase; }
    static int instanceCount() { return count; }
protected:
    int iBase;
    static int count;
};

// 測試函數通過地址運算解析 vptr、虛表項、數據成員位置
// 輸出驗證: 對象首地址 = vptr; vptr[0] = 析構函數; vptr[1] = print(); 數據成員緊隨 vptr 後

繼承場景下的模型變化

單繼承 (無重寫)

  • 派生類**擴充基類虛表** (不新增 vptr)
  • 內存佈局:[vptr][Base::iBase][Derived::iDerived]
  • 對象大小不變 (僅 1 個 vptr)

單繼承 (有重寫)

  • 虛表中對應槽位**替換為派生類函數地址**
  • 基類指針指向派生對象時,虛函數調用自動分發到派生類實現

多繼承

  • 每個基類有**獨立虛表**
  • 派生類成員函數放入**第一個基類虛表**
  • 內存佈局:[vptr_Base1][Base1數據][vptr_Base2][Base2數據][Derived數據]
  • **關鍵**:所有基類虛表中同名虛函數槽位**均替換為派生類實現**,確保任意基類指針調用正確

虛繼承

  • 解決菱形繼承重複基類問題
  • 派生類有**獨立虛表**,基類虛表**單獨保存**,中間以 0x0 分隔
  • 佈局:[vptr_Derived][Derived數據][0x0][vptr_Base][Base數據]
  • 若派生類無虛函數,無 vptr,但仍需 0x0 分隔
  • 對象大小顯著增大 (如示例 12 字節 vs 4 字節)

核心機制深度解析

成員訪問

  • 非靜態數據:直接取址 (對象基址 + 偏移量,編譯期確定)
  • 靜態/非虛函數:靜態綁定 (編譯期確定地址)
  • 虛函數:動態綁定 (運行期:vptr → 虛表 → 函數地址,雙重間接)

多態實現本質

Base* p = new Derived();
p->print_virtual(); // 動態綁定: p->vptr -> Derived虛表 -> Derived::print_virtual()
p->print();         // 靜態綁定: Base::print() (非虛函數)

**必要條件**:① 虛函數 ② 基類指針/引用指向派生對象 ③ 派生類重寫 (override)

虛析構函數必要性

Base* p = new Derived();
delete p; // 若 ~Base() 非虛: 僅調用 ~Base() → 內存洩漏!
          // 若 ~Base() 虛: 調用 ~Derived() -> ~Base() 正確釋放

模型視角:非虛析構不在虛表中,派生類無法 override,刪除基類指針時無法分發到派生類析構函數。

對象大小驗證

class Base { virtual void f(); };           // sizeof = 4/8 (vptr)
class Derived : public Base {};              // sizeof = 4/8 (共用 vptr)
class VDerived : virtual public Base {};     // sizeof = 12/24 (vptr + vbptr + padding)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved