| 條款 | 核心觀點 | 關鍵詞 |
|---|---|---|
| 32 | public 繼承 = is-a 關系 | 里氏替換原則 |
| 33 | 避免遮掩繼承名稱 | using 聲明 / 轉交函數 |
| 34 | 區分接口繼承與實現繼承 | pure virtual / impure virtual / non-virtual |
| 35 | 考慮 virtual 替代方案 | NVI / Strategy / Function Pointer |
| 36 | 絕不重新定義 non-virtual 函數 | 靜態綁定 vs 動態綁定 |
| 37 | 絕不重新定義繼承的缺省參數 | 缺省參數靜態綁定 |
| 38 | 復合表達 has-a / implemented-in-terms-of | 優先復合過繼承 |
| 39 | 審慎使用 private 繼承 | 訪問 protected / 空基優化 |
| 40 | 審慎使用多重繼承 | 虛繼承解決鑽石問題 |
**定義**:`public` 繼承意味著「是一個」。基類契約 (介面、不變式、語義) 必須完整適用於派生類。
**反例**:`Penguin : public Bird`,若 `Bird::fly()` 存在,企鵝違反契約。正確建模應抽象出 `FlyingBird` 或用特徵類。
派生類同名函數會**遮掩**基類**所有**同名重載:
class Base { virtual void mf1(); virtual void mf1(int); void mf3(); void mf3(double); };
class Derived : public Base { virtual void mf1(); void mf3(); };
Derived d; d.mf1(10); // 錯誤!Derived::mf1 遮掩了 Base::mf1(int)
| 函數類型 | 接口繼承 | 實現繼承 |
|---|---|---|
| pure virtual | ✓ | ✗ (強制派生類實現) |
| impure virtual | ✓ | ✓ (提供缺省實現,可覆蓋) |
| non-virtual | ✓ | ✓ (強制繼承實現,不可覆蓋) |
class GameCharacter {
public:
int healthValue() const { // public non-virtual
// 前置檢查、日誌、鎖...
int ret = doHealthValue(); // 調用 private virtual
// 後置處理...
return ret;
}
private:
virtual int doHealthValue() const = 0; // 派生類實現
};
**優勢**:基類控制調用前後邏輯,派生類只關注核心算法。
將算法封裝為可替換對象:
**優勢**:運行時動態切換策略,同一對象不同階段可用不同算法。
non-virtual = **靜態綁定** (編譯期根據指針/引用類型決定);virtual = **動態綁定** (運行期根據對象類型決定)。
Base* pb = new Derived;
pb->nonVirtualFunc(); // 永遠調用 Base::nonVirtualFunc()!
**後果**:違反 is-a,導致行為不一致。
**缺省參數靜態綁定**,virtual 函數動態綁定 → **靜態類型決定缺省值,動態類型決定函數體**。
class Shape { virtual void draw(Color c = Red) = 0; };
class Rect : public Shape { void draw(Color c = Green) override; };
Shape* ps = new Rect;
ps->draw(); // 調用 Rect::draw(),但缺省參數是 Red!
class Shape {
public:
void draw(Color c = Red) const { doDraw(c); } // 缺省參數只在這裡
private:
virtual void doDraw(Color c) const = 0; // 無缺省參數
};
**繼承 = is-a** (類型關系);**復合 = has-a / implemented-in-terms-of** (實現關系)。
**例**:`Set` 不應繼承 `List` (List 允許重複,Set 不允許,違反 is-a);應包含 `List` 成員並封裝去重邏輯。
`private` 繼承 = **implemented-in-terms-of** (按某物實現)。適用場景:
**一般情況優先復合**,private 繼承耦合度更高。
class File { string name; };
class InputFile : public File { ... };
class OutputFile : public File { ... };
class IOFile : public InputFile, public OutputFile { ... }; // 兩份 name!
class InputFile : virtual public File { ... };
class OutputFile : virtual public File { ... };
class IOFile : public InputFile, public OutputFile { ... }; // 單份 name
`public` 繼承介面類 + `private` 繼承實現輔助類 = **介面繼承 + 實現復用** 的組合。