C++使用繼承時子對象的內存佈局
1 示例程序
class A { protected: int a; public: A() : a(1) {} virtual void a1() {} virtual void a2() {} }; class B { protected: int b; public: B() : b(2) {} virtual void b1() {} virtual void b2() {} }; class C : public A, public B { protected: int c; public: C() : c(3) {} virtual void a1() {} virtual void b1() {} }; int _tmain(int argc, _TCHAR* argv[]) { C *c = new C; B *b = (B *)c; return 0; }
類 A/B/C 之間的關係以下:less
2 對象的內存佈局
C *c = new C();
在上面這段代碼中,對象 c 的內存佈局以下:ide
低地址 | ||||
… | ||||
1. 虛表指針 | –> | &C::a1 | &A::a2 | . |
2. A::a | ||||
3. 虛表指針 | –> | &C::b1 | &B::b2 | . |
4. B::b | ||||
5. c::c | ||||
… | ||||
高地址 |
c 對象從低地址到高地址依次存儲了:A 的虛表指針、A 的成員變量、B 的虛表指針、B 的成員變量、C 的成員變量。咱們發現,虛函數表 A 中的 A::a1 函數被 C::a1 函數覆蓋了,虛函數表 B 中 B::b1 函數被 C::b1 覆蓋了,這是由於 C 重寫了 a1 和 b1方法。這意味着經過 c 對象中的虛表指針調用 a1 或 b1 函數,只可以調用到 C 類中重寫的方法。函數
C *c = new C; c->b1(); B *b = c; b->b1();
上面的兩句調用都會調用 C 的 b1 方法。另外,在對 b 賦值時,b 獲得的結果並非 c 的值,而是 c+8,這正好是 c 對象中用於存儲 B 虛表指針的地址。佈局