# 🔮 多型 Polymorphism --- - **學習目標:** - 認識多型的基本觀念與用途 - 學習虛擬函式的使用方法 - 運用多型設計更彈性的程式架構 --- ## 🔖 什麼是多型? - 多型(`Polymorphism`): - **同一個函式呼叫,可根據物件類型不同而執行不同的動作。** - 實現方式: - 使用**虛擬函式 (`virtual function`)** --- ## 📌 虛擬函式 (Virtual Function) - 使用關鍵字 `virtual` 在基底類別定義函式 - 衍生類別可覆寫基底類別的虛擬函式(override) ---- ## 基本語法: ```cpp class Base { public: virtual void show() { cout << "Base show\n"; } }; class Derived : public Base { public: void show() override { // 覆寫函式 cout << "Derived show\n"; } }; ``` ---- ## override 解釋 - In C++,當你在衍生類別實作來自虛擬基類(virtual class)的函式時,override 關鍵字不是必須的,但強烈建議使用。 ---- ### 1️⃣ override 的用途 - 編譯器檢查: - 如果你打算覆寫(override)基類的虛擬函式,但簽名寫錯(例如參數不同、const 漏掉),加了 override 後編譯器會直接報錯。 - 可讀性: - 讓程式碼的維護者一眼看出「這是覆寫自基類的虛擬函式」,而不是新的成員函式。 ---- ### 2️⃣ 不使用 override 的情況 - 功能上: - 只要函式簽名與基類虛擬函式完全相同,它還是會成功覆寫,執行期會透過 vtable 呼叫到你的版本。 - 風險: - 如果簽名稍有不同(哪怕只是 const 或 &),編譯器不會幫你檢查 → 你以為覆寫了,實際上只是新增了一個全新的函式,導致多型失效。 ---- ## 🚩 多型範例 ```cpp Base *bptr; Base base; Derived derived; bptr = &base; bptr->show(); // Base show bptr = &derived; bptr->show(); // Derived show (多型) ``` 呼叫的函式依物件真正的型別決定(`動態繫結`) --- ## 📗 多型與指標陣列應用 ```cpp Base* arr[2]; arr[0] = new Base(); arr[1] = new Derived(); for(int i=0; i<2; i++) arr[i]->show(); // 輸出: // Base show // Derived show ``` --- ## 🚩 純虛擬函式(Pure Virtual Function) - 純虛擬函式沒有實作內容,表示必須由衍生類別實作 - 包含純虛擬函式的類別稱為 抽象類別 (Abstract Class) - 語法: ```cpp class Shape { public: virtual void draw() = 0; // 純虛擬函式 }; ``` ---- - 純虛擬函式(Pure Virtual Function)在 Java 中又稱 interface - 可以把它想像成給後續使用者建立的規範或接口 --- ## 📌 抽象類別與具體類別 - 抽象類別(Abstract Class):不可直接建立物件 - 具體類別(Concrete Class):可建立物件,必須實作所有純虛擬函式 ```cpp class Circle : public Shape { public: void draw() override { cout << "畫圓形\n"; } }; Circle c; c.draw(); // 畫圓形 ``` --- | 項目 | 純虛函數 | 抽象類別 | |:------------ | --------------------------------------- | ------------------------------ | | 定義 | 沒有實作的虛擬函式,宣告時以 `= 0` 標示 | 至少包含一個純虛函式的類別 | | 是否可實例化 | 不可實例化 | 不可實例化 | | 作用 | 定義介面,強制衍生類一定要實作 | 作為介面與基類,提供多型的支援 | ---- | 項目 | 純虛函數 | 抽象類別 | | ---------------- | ---------------------- |:---------------------------------- | | 是否必須被實作 | 衍生類必須實作純虛函數 | 衍生類若未實作純虛函數,仍是抽象類 | | 是否可有函數實作 | 不可有函數體 | 可有普通函數與資料成員 | --- ## 📗 多型設計的好處 1. 增加程式彈性 2. 程式易於擴充 3. 更容易維護與管理不同類型的物件 --- ## 🚩 多型經典應用:圖形繪製範例 ```cpp class Shape { public: virtual void draw() = 0; }; class Rectangle : public Shape { public: void draw() override { cout << "畫矩形\n"; } }; class Triangle : public Shape { public: void draw() override { cout << "畫三角形\n"; } }; ``` --- ## 使用方式 ```cpp Shape* shapes[2] = {new Rectangle(), new Triangle()}; for(auto shape : shapes) shape->draw(); // 輸出: // 畫矩形 // 畫三角形 ``` --- ## 📌 虛擬解構子(Virtual Destructor) - 基底類別應提供虛擬解構子,確保正確解構衍生類別物件 ```cpp class Base { public: virtual ~Base() { cout << "Base解構\n"; } }; class Derived : public Base { public: ~Derived() { cout << "Derived解構\n"; } }; ``` ---- ## 使用方式 ```cpp Base *ptr = new Derived(); delete ptr; // 輸出: // Derived解構 // Base解構 ``` ---- ## 沒有虛擬解構子會怎樣? - 如果基底類別解構子不是 virtual,當你用基底類別指標刪除指向衍生類別的物件時,只會呼叫基底類別的解構子,衍生類別部分不會被釋放 - `→ 資源洩漏或未完成清理。` ---- ```cpp #include <iostream> using namespace std; class Base { public: ~Base() { cout << "Base 解構\n"; } }; class Derived : public Base { public: ~Derived() { cout << "Derived 解構\n"; } }; int main() { Base* ptr = new Derived(); delete ptr; // 只呼叫 Base 解構子 } // 輸出: // Base 解構 ``` ---- - 上面變成 function shallowing,只有解構 Base - -> 記憶體洩漏 ---- - Constructor 能不能 virtual? --- ## ⚠️ 多型注意事項 - 透過基底類別指標或參考呼叫虛擬函式才會啟動多型行為 - 基底類別必須提供虛擬解構子,以防記憶體洩漏 --- ## 進階 vtable [C++ Virtual & vtable](/922nZg9UTbSyzl43PNfq_Q) --- ## 🛠️ 課堂練習 (Lab) - 練習一: - 實作 Payment system - A CreditCardPayment is a Payment. - A PayPalPayment is a Payment. - A CashPayment is a Payment. - 練習二: - 實作抽象類別 Employee,衍生出類別 Manager 和 Worker,透過多型輸出各自職務資訊。 --- ## 💡 本週作業 - 將上次作業延伸,完成動物園管理系統
{"title":"🔮 多型 Polymorphism","description":"學習目標:","contributors":"[{\"id\":\"01487228-6720-47a9-875f-2f01b5d455ad\",\"add\":4455,\"del\":187,\"latestUpdatedAt\":1755230948424}]"}
    64 views