C++ 從基礎到精通 — 第 8 講
繼承和多態性、物件生命週期和 RAII
該部落格上撰寫的文章是出於教學目的而創建的。 另外,文章中的資訊可能不正確,請謹慎。
戰士和弓箭手都是角色,但攻擊方式不同。 通用的資料可以放在父類別中,不同的操作可以在子類別中實現。
繼承與多型
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;
現在我們需要智慧指針,允許指針在擁有物件時自動清理它們。