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

C++ 對象內存佈局深度教程:從單繼承到鑽石虛繼承

     編輯:關於C語言

C++ 對象模型是理解多態、繼承、性能優化的基石。本文系統剖析各類繼承場景下的對象內存佈局,含虛函數表指針、成員變量排列、虛繼承 vbptr/vtable 機制,基於 VC++ 2003 與 G++ 3.4.4 實測。

1. 影響對象佈局的六大因素

  1. 非靜態成員變量
  2. 虛函數(產生 vptr/vtable)
  3. 單一繼承
  4. 多重繼承
  5. 重復繼承(菱形非虛繼承)
  6. 虛擬繼承(解決重復基類)

不討論編譯器優化與字節對齊,聚焦語言語義層面。

2. 單一一般繼承(含虛函數覆蓋)

class Parent { int iparent; virtual void f(); virtual void g(); virtual void h(); };
class Child : public Parent { int ichild; virtual void f(); virtual void g_child(); };
class GrandChild : public Child { int igrandchild; virtual void f(); virtual void g_child(); };

佈局(VC++/G++ 一致):

[0] vptr → GrandChild::f, Parent::g, Parent::h, GrandChild::g_child, Child::h_child, GrandChild::h_grandchild
[1] Parent::iparent
[2] Child::ichild
[3] GrandChild::igrandchild
  • vptr 位於對象首地址
  • 成員按聲明順序、繼承順序線性排列
  • 覆蓋的虛函數在 vtable 中替換原位置

3. 多重繼承(三個基類)

class Base1 { int ib1; virtual void f(); g(); h(); };
class Base2 { int ib2; virtual void f(); g(); h(); };
class Base3 { int ib3; virtual void f(); g(); h(); };
class Derive : public Base1, public Base2, public Base3 { int id; virtual void f(); g1(); };

佈局:

[0] Base1::vptr → Derive::f, Base1::g, Base1::h, Derive::g1, nullptr
[1] Base1::ib1
[2] Base2::vptr → Derive::f, Base2::g, Base2::h, nullptr
[3] Base2::ib2
[4] Base3::vptr → Derive::f, Base3::g, Base3::h, nullptr
[5] Base3::ib3
[6] Derive::id
  • 每個基類有獨立 vptr
  • 子類新增虛函數放入第一個基類 vtable
  • 所有基類 vtable 中同名虛函數均被覆蓋為派生類版本(多態正確性)

4. 重復繼承(菱形非虛繼承)

class B { int ib; char cb; virtual void f(); Bf(); };
class B1 : public B { int ib1; char cb1; virtual void f(); f1(); Bf1(); };
class B2 : public B { int ib2; char cb2; virtual void f(); f2(); Bf2(); };
class D : public B1, public B2 { int id; char cd; virtual void f(); f1(); f2(); Df(); };

佈局關鍵:D 包含兩份 B 子對象(分別來自 B1、B2),導致數據冗餘與二義性:

D::B1::vptr → D::f, B1::Bf1, D::f1, D::Df, nullptr
B::ib, B::cb, B1::ib1, B1::cb1
D::B2::vptr → D::f, B2::Bf2, D::f2, nullptr
B::ib, B::cb, B2::ib2, B2::cb2
D::id, D::cd

訪問 d.ib 報二義性錯誤,需顯式指定 d.B1::ib 或 d.B2::ib。

5. 鑽石型虛繼承


class B1 : virtual public B { ... };
class B2 : virtual public B { ... };
class D : public B1, public B2 { ... };

VC++ 佈局:

[0] D::B1::vptr
[1] B1::ib1, B1::cb1
[2] D::B2::vptr
[3] B2::ib2, B2::cb2
[4] D::id, D::cd
[5] NULL 分隔符
[6] B::vptr (共享基類)
[7] B::ib, B::cb
  • 虛基類 B 只有一份實例,放在最末尾
  • VC++ 用 NULL 分隔;GCC 通過偏移量計算定位
  • 各路徑 vptr 指向同一虛基類 vtable

6. 實測代碼模板


typedef void(*Fun)();
int** pVtab = (int**)&obj;
for (int i=0; (Fun)pVtab[0][i]!=NULL; i++) {
    ((Fun)pVtab[0][i])(); // 調用虛函數
}

總結

C++ 對象模型精妙複雜:單繼承線性、多重獨立 vptr、重復冗餘、虛繼承共享延後。理解佈局有助於調試多態異常、優化內存、設計 ABI 兼容庫。現代 C++ 開發雖少直接操作內存,但「知其然更知其所以然」是高手必修課。

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