抽象的本質是 「用相同的行為操作不同的對象」。在 C++ 中,這通過 類別繼承體系 + 虛函數機制 實現:定義一個抽象基類宣告介面,派生類實作具體行為,使用者持有基類指標/參考操作派生物件,實現「介面與實作分離」。
Connect/Send/Recv/Close 提煉為抽象基類 INetworkProtocol,各協議派生實作。這說明抽象是 迭代、互動、隨認識深化而調整 的過程,非一蹴而就。
class Parent {
public:
virtual ~Parent() {} // 1. 虛解構函數:保證正確銷毀派生物件
virtual void GetValue() = 0; // 2. 純虛函數:強制派生類實作
virtual void Test() { /*預設實作*/ } // 3. 普通虛函數:提供預設行為
};
class Child1 : public Parent {
public:
virtual ~Child1() {}
virtual void GetValue() override { /*實作*/ }
virtual void Test() const override { /*實作*/ } // const 版本 = 隱藏而非覆寫!
};
class Child2 : public Parent {
public:
virtual ~Child2() {}
virtual void GetValue() override { /*實作*/ }
virtual void Test() override { /*實作*/ }
};
| 規則 | 說明 | 違反後果 |
|---|---|---|
| 1. 必須透過繼承關係 | 被抽象對象需直接/間接繼承自共同基類 | 無法以基類指標統一操作 |
| 2. 基類必須宣告虛函數 | 若需多型呼叫,基類必須有對應虛函數宣告(否則需 down_cast,失去類型安全) | 違反開放封閉原則,維護困難 |
| 3. 基類解構函數必須為 virtual | 刪除基類指標時正確調用派生解構函數釋放資源 | 資源洩漏、未定義行為 |
純虛類別(無資料成員、無建構函數、所有方法純虛),模擬 Java/C# 介面:
class ISerializable {
public:
virtual ~ISerializable() {}
virtual void Save(IStream*) = 0;
virtual void Load(IStream*) = 0;
};
基類定義演算法骨架,延遲關鍵步驟到派生類:
class Algorithm {
void Run() { Step1(); Step2(); Step3(); } // 非虛,鎖定流程
virtual void Step1() = 0;
virtual void Step2() = 0;
virtual void Step3() = 0;
};
不想用繼承時,可用「類型擦除」封裝任意可呼叫物件:
class Drawable {
struct Concept { virtual ~Concept() {} virtual void Draw() = 0; };
template struct Model : Concept {
T obj; Model(T o):obj(std::move(o)) {}
void Draw() override { obj.Draw(); }
};
std::unique_ptr p;
public:
template Drawable(T o) : p(new Model(std::move(o))) {}
void Draw() { p->Draw(); }
};
C++ 抽象的核心是 「虛函數表 + 基類指標/參考」 實現的執行期多型。良好的抽象設計遵循 依賴倒置原則:高層模組依賴抽象介面,低層模組實作介面。掌握虛函數機制、虛解構函數必要性、介面類別設計、模板方法模式,並警惕構造/解構期虛函數行為差異,即可駕馭 C++ 大型專案的架構設計。