← 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;

现在我们需要智能指针,允许指针在拥有对象时自动清理它们。