S SmartDocs
Series: C++ cpp 273 lines · Updated 2026-04-03

polymorphism.cpp

C++/Part2_物件導向/Ch10_繼承與多型/polymorphism.cpp

// ============================================================
// Ch10 — polymorphism.cpp
// 多型:虛擬函式、vtable 概念、虛擬解構子、override、
//       基礎類別指標指向衍生物件的執行期多型
// 編譯:g++ -std=c++17 -Wall -o polymorphism polymorphism.cpp
// ============================================================

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <cmath>

// ────────────────────────────────────────────────────────────
// 1. Shape 階層 — 完整的多型示範
// ────────────────────────────────────────────────────────────

class Shape {
public:
    Shape(const std::string& name) : name_(name) {}

    // 虛擬函式:衍生類別可以覆寫
    virtual double area() const { return 0.0; }

    virtual double perimeter() const { return 0.0; }

    virtual void draw() const {
        std::cout << "  [Shape] 繪製 " << name_ << "\n";
    }

    // 虛擬解構子:確保透過基礎類別指標 delete 時正確解構
    virtual ~Shape() {
        std::cout << "  [~Shape] " << name_ << "\n";
    }

    const std::string& name() const { return name_; }

protected:
    std::string name_;
};

// ── Circle ──

class Circle : public Shape {
public:
    Circle(double radius)
        : Shape("圓形"), radius_(radius) {}

    double area() const override {
        return M_PI * radius_ * radius_;
    }

    double perimeter() const override {
        return 2.0 * M_PI * radius_;
    }

    void draw() const override {
        std::cout << "  ● 繪製圓形 (半徑=" << radius_ << ")\n";
    }

    ~Circle() override {
        std::cout << "  [~Circle] 半徑=" << radius_ << "\n";
    }

private:
    double radius_;
};

// ── Rectangle ──

class Rectangle : public Shape {
public:
    Rectangle(double w, double h)
        : Shape("長方形"), width_(w), height_(h) {}

    double area() const override {
        return width_ * height_;
    }

    double perimeter() const override {
        return 2.0 * (width_ + height_);
    }

    void draw() const override {
        std::cout << "  ▬ 繪製長方形 (" << width_
                  << " x " << height_ << ")\n";
    }

    ~Rectangle() override {
        std::cout << "  [~Rectangle] " << width_ << "x" << height_ << "\n";
    }

private:
    double width_, height_;
};

// ── Triangle ──

class Triangle : public Shape {
public:
    Triangle(double a, double b, double c)
        : Shape("三角形"), a_(a), b_(b), c_(c) {}

    double area() const override {
        // 海龍公式
        double s = (a_ + b_ + c_) / 2.0;
        return std::sqrt(s * (s - a_) * (s - b_) * (s - c_));
    }

    double perimeter() const override {
        return a_ + b_ + c_;
    }

    void draw() const override {
        std::cout << "  △ 繪製三角形 (邊長: "
                  << a_ << ", " << b_ << ", " << c_ << ")\n";
    }

    ~Triangle() override {
        std::cout << "  [~Triangle]\n";
    }

private:
    double a_, b_, c_;
};

// ────────────────────────────────────────────────────────────
// 2. 虛擬解構子對比實驗
// ────────────────────────────────────────────────────────────

class BaseNoVirtual {
public:
    BaseNoVirtual()  { std::cout << "  [BaseNoVirtual] 建構\n"; }
    ~BaseNoVirtual() { std::cout << "  [BaseNoVirtual] 解構\n"; }
    // ↑ 非虛擬解構子
};

class DerivedNoVirtual : public BaseNoVirtual {
public:
    DerivedNoVirtual()  { std::cout << "  [DerivedNoVirtual] 建構\n"; }
    ~DerivedNoVirtual() { std::cout << "  [DerivedNoVirtual] 解構\n"; }
};

class BaseVirtual {
public:
    BaseVirtual()          { std::cout << "  [BaseVirtual] 建構\n"; }
    virtual ~BaseVirtual() { std::cout << "  [BaseVirtual] 解構\n"; }
};

class DerivedVirtual : public BaseVirtual {
public:
    DerivedVirtual()          { std::cout << "  [DerivedVirtual] 建構\n"; }
    ~DerivedVirtual() override { std::cout << "  [DerivedVirtual] 解構\n"; }
};

// ────────────────────────────────────────────────────────────
// 3. 展示 override 防止拼字錯誤
// ────────────────────────────────────────────────────────────

class Instrument {
public:
    virtual void play() const { std::cout << "  ♪ Instrument 演奏\n"; }
    virtual ~Instrument() = default;
};

class Piano : public Instrument {
public:
    void play() const override { std::cout << "  ♫ Piano 演奏鋼琴曲\n"; }
    // 若寫成 void Play() const override → 編譯錯誤!拼字錯誤被抓到
};

class Violin : public Instrument {
public:
    void play() const override { std::cout << "  ♫ Violin 演奏小提琴曲\n"; }
};

// ============================================================
// main
// ============================================================

int main() {
    std::cout << "========================================\n";
    std::cout << " 1. 基礎類別指標的多型行為\n";
    std::cout << "========================================\n";
    {
        // 使用 unique_ptr 管理記憶體,基礎類別有虛擬解構子
        std::vector<std::unique_ptr<Shape>> shapes;
        shapes.push_back(std::make_unique<Circle>(5.0));
        shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0));
        shapes.push_back(std::make_unique<Triangle>(3.0, 4.0, 5.0));

        std::cout << "\n--- 透過 Shape* 呼叫虛擬函式 ---\n";
        double totalArea = 0.0;
        for (const auto& s : shapes) {
            s->draw();                          // 執行期多型
            std::cout << "    面積 = " << s->area()
                      << ", 周長 = " << s->perimeter() << "\n";
            totalArea += s->area();
        }
        std::cout << "\n  所有圖形總面積 = " << totalArea << "\n";

        std::cout << "\n--- unique_ptr 離開作用域,觀察虛擬解構子 ---\n";
    }

    std::cout << "\n========================================\n";
    std::cout << " 2. 虛擬解構子的重要性\n";
    std::cout << "========================================\n";
    {
        std::cout << "\n--- 無虛擬解構子 ---\n";
        BaseNoVirtual* p1 = new DerivedNoVirtual();
        delete p1;   // 只呼叫 BaseNoVirtual 解構子!衍生類別解構子被跳過

        std::cout << "\n--- 有虛擬解構子 ---\n";
        BaseVirtual* p2 = new DerivedVirtual();
        delete p2;   // 正確呼叫 DerivedVirtual → BaseVirtual
    }

    std::cout << "\n========================================\n";
    std::cout << " 3. vtable 觀念驗證\n";
    std::cout << "========================================\n";
    {
        Circle c(3.0);
        Rectangle r(2.0, 5.0);

        // sizeof 包含 vptr 的大小
        std::cout << "  sizeof(Shape)     = " << sizeof(Shape) << " bytes\n";
        std::cout << "  sizeof(Circle)    = " << sizeof(c) << " bytes\n";
        std::cout << "  sizeof(Rectangle) = " << sizeof(r) << " bytes\n";
        std::cout << "  (含有虛擬函式的物件會多一個 vptr)\n";
    }

    std::cout << "\n========================================\n";
    std::cout << " 4. override 關鍵字與樂器多型\n";
    std::cout << "========================================\n";
    {
        std::vector<std::unique_ptr<Instrument>> band;
        band.push_back(std::make_unique<Piano>());
        band.push_back(std::make_unique<Violin>());
        band.push_back(std::make_unique<Piano>());

        std::cout << "\n  樂團演奏:\n";
        for (const auto& inst : band) {
            inst->play();   // 多型:各樂器各自演奏
        }
    }

    std::cout << "\n========================================\n";
    std::cout << " 5. 用原始指標示範多型(搭配手動 delete)\n";
    std::cout << "========================================\n";
    {
        Shape* shapes[] = {
            new Circle(2.0),
            new Rectangle(3.0, 7.0),
            new Triangle(5.0, 12.0, 13.0)
        };

        for (auto* s : shapes) {
            s->draw();
            std::cout << "    面積 = " << s->area() << "\n";
        }

        std::cout << "\n--- delete 各物件 ---\n";
        for (auto* s : shapes) {
            delete s;   // 虛擬解構子確保完整解構
        }
    }

    std::cout << "\n========================================\n";
    std::cout << " 程式結束\n";
    std::cout << "========================================\n";

    return 0;
}

Related articles