C++ 標準僅規定「非靜態成員依宣告順序配置」,具體佈局由編譯器決定(ABI)。本文以 MSVC 6.0 為例,透過取址輸出與 Placement New 實驗,直觀展示單一繼承、多重繼承下的資料成員排列、vptr 位置與數量,助你理解物件模型本質。
class Point2d { public: int _x, _y; virtual void f() {} }; // 1 vptr
class Point3d : public Point2d { public: int _z; }; // 單繼承
class Vertex { public: int next; virtual void h() {} }; // 1 vptr
class Vertex3d : public Point3d, public Vertex { public: int mumble; }; // 多重繼承
&v3d = 0x0012ff64 ← Vertex3d 起始位址 = Point3d 子物件 = Point2d 子物件
&_x = 0x0012ff68 ← Point2d::_x
&_y = 0x0012ff6c ← Point2d::_y
&_z = 0x0012ff70 ← Point3d::_z
&pv = 0x0012ff74 ← Vertex 子物件起始 = 第二個 vptr
&next = 0x0012ff78 ← Vertex::next
&mumble = 0x0012ff7c ← Vertex3d::mumble
Vertex3d (sizeof = 28 bytes)
├── Point2d 子物件 (offset 0)
│ ├── vptr #1 → Point2d vtable
│ ├── _x (4)
│ └── _y (4)
├── Point3d 擴充 (offset 12)
│ └── _z (4)
├── Vertex 子物件 (offset 16)
│ ├── vptr #2 → Vertex vtable
│ └── next (4)
└── Vertex3d 自身 (offset 24)
└── mumble (4)
vptr。class D : public B1, public B2 → B1 在低位址,B2 緊隨其後。mumble 排在所有基類子物件之後。Vertex* pv = &v3d 時,編譯器自動加上 offsetof(Vertex3d, Vertex) (16 bytes)。long storage[30];
Vertex3d* p = new (storage) Vertex3d; // 在陣列上建構物件
for (int i=0; i
vptr,次要基類用 vtable offset 方式,佈局不同。vptr 8 位元組,對齊規則更嚴格。std::byte 序列化。「理解物件佈局不是為了鑽標準漏洞,而是為了除錯多重繼承切割、效能分析快取命中、以及與 C/組語互操作時的底氣。」