← SEARCHABLE ARCHIVE
C / C++ · 2026-06-12

C++ 從基礎到精通 — 第 8 講

繼承和多態性、物件生命週期和 RAII

INTERACTIVE READING MODE →

該部落格上撰寫的文章是出於教學目的而創建的。 另外,文章中的資訊可能不正確,請謹慎。

戰士和弓箭手都是角色,但攻擊方式不同。 通用的資料可以放在父類別中,不同的操作可以在子類別中實現。

繼承與多型

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

    virtual ~Character() = default;
    virtual int Attack() const = 0;

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

private:
    std::string name;
};

class Warrior : public Character
{
public:
    Warrior(const std::string& name)
        : Character(name)
    {
    }

    int Attack() const override
    {
        return 20;
    }
};

class Archer : public Character
{
public:
    Archer(const std::string& name)
        : Character(name)
    {
    }

    int Attack() const override
    {
        return 15;
    }
};

Attack() = 0具有純虛函數而沒有實現的類別成為抽象類別。 角色本身無法創建,子類別被迫實施攻擊。

override如果新增 ,則當簽章與父函式的簽章不同時,編譯器會擷取錯誤。 這可以防止導致新函數被視為拼字錯誤的錯誤。

即使使用父指標呼叫 Attack,也會根據實際物件是 Warrior 還是 Archer 來執行不同的函數。 這就是運行時多態性。

虛擬析構函數

將父類別的析構函數留給虛繼承是安全的。

virtual ~Character() = default;

透過父指標刪除子物件時,如果沒有虛析構函數,則可能不會呼叫子析構函數。 如果子進程持有檔案或記憶體等資源,則可能會錯過清理。

物件生命週期和 RAII

本地物件在區塊末尾自動銷毀。

void Battle()
{
    Warrior warrior{"Rose"};
    std::cout << warrior.Attack() << '\n';
} //這裡,呼叫了 Warriors 的析構函數。

將物件建立和資源取得、物件銷毀和資源釋放連結起來的方法稱為RAII。 即使在函數中間傳回,也會呼叫本地物件的析構函數。

晚安。現在,您只需查看程式碼即可知道誰組織了哪些內容以及何時組織。 另一方面new和delete如果它們散落各處,就很難知道誰擁有它們。

需要多態性的物件不應該作為值儲存在父類型中。 std::vector<Character>輸入 Warrior 作為值可能會導致物件切片,其中子部件被切除。

因此,它必須以指標形式存儲,如下所示。

std::vector<std::unique_ptr<Character>> party;

現在我們需要智慧指針,允許指針在擁有物件時自動清理它們。