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

C++ 虛繼承深度解析:菱形繼承問題、記憶體佈局與虛表機制

     編輯:關於SqlServer

菱形繼承問題

class Animal { public: void Op() { cout << "Animal"; } };
class Tiger  : public Animal { public: void Tg() { cout << "Tiger"; } };
class Lion   : public Animal { public: void Lo() { cout << "Lion"; } };
class Liger  : public Tiger, public Lion { public: void Lo() { cout << "Liger"; } };

int main() {
    Liger lg;
    lg.Op();  // ❌ 編譯錯誤:request for member 'Op' is ambiguous
}

原因:Liger 含兩份 Animal 子物件(分別來自 Tiger、Lion),呼叫 Op() 時編譯器不知選哪份。

記憶體佈局對比

普通繼承 (無 virtual)

Liger 物件佈局 (低址→高址):
1. Animal (Tiger 路徑) 成員
2. Tiger  成員 (含 Op)
3. Animal (Lion 路徑) 成員   ← 第二份 Animal
4. Lion   成員 (含 Op)
5. Liger  自身成員

虛繼承 (加 virtual)

class Tiger : public virtual Animal { ... };
class Lion  : public virtual Animal { ... };
Liger 物件佈局 (低址→高址):
1. Animal 成員 (共享單一份)     ← 由最衍生類 Liger 決定位置
2. Tiger  成員 (不含 Animal 部分)
3. Lion   成員 (不含 Animal 部分)
4. Liger  自身成員

關鍵:虛基類子物件由最衍生類決定位置並共享唯一份,消除歧義。

虛表機制補充

  • 有虛函數的類 → 物件首部隱藏 vptr 指向虛表 (函數指針陣列)
  • 虛繼承引入 vbptr (虛基類表指針) 或在虛表中增加偏移量項,用於運行期定位共享虛基類位置
  • 成員函數編譯為全域函數:lg.Op() → Animal::Op(&lg),this 指針隱式傳遞

虛繼承最佳實踐

  1. 介面類 (純虛函數) 建議虛繼承,避免菱形
  2. 資料成員 盡量放在最衍生類或非虛路徑,避免共享狀態混淆
  3. 建構/解構順序:虛基類由最衍生類最先建構、最後解構,非虛基類按宣告順序
  4. 效能代價:額外指針間接存取、物件體積增大、建構複雜度上升 → 僅在必要時使用

經典面試題

Q: 虛繼承下,sizeof(Liger) 比普通繼承大還是小?
A: 通常較大。雖省去一份 Animal,但引入 vbptr/虛表偏移量,且對齊填充可能增加。

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