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

C++ von den Grundlagen bis zum Profi — Folge 8

Vererbung und Polymorphismus, Objektlebensdauer und RAII

INTERACTIVE READING MODE →

Die in diesem Blog verfassten Artikel wurden mit dem Ziel erstellt, zu unterrichten. Außerdem können die Informationen im Artikel falsch sein, also seien Sie bitte vorsichtig.

Krieger und Bogenschütze sind beide Charaktere, aber ihre Angriffsmethoden sind unterschiedlich. Gemeinsame Daten können in der übergeordneten Klasse platziert werden und in der untergeordneten Klasse können unterschiedliche Operationen implementiert werden.

Vererbung und Polymorphismus

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() = 0Eine Klasse mit rein virtuellen Funktionen ohne Implementierung wird zu einer abstrakten Klasse. Der Charakter selbst kann nicht erstellt werden und untergeordnete Klassen sind gezwungen, Attack zu implementieren.

overrideWenn Sie hinzufügen, fängt der Compiler einen Fehler ab, wenn sich die Signatur von der Signatur der übergeordneten Funktion unterscheidet. Dadurch können Fehler vermieden werden, die dazu führen, dass eine neue Funktion als Rechtschreibfehler behandelt wird.

Selbst wenn Sie Attack mit dem übergeordneten Zeiger aufrufen, werden unterschiedliche Funktionen ausgeführt, je nachdem, ob das eigentliche Objekt Warrior oder Archer ist. Dies ist Laufzeitpolymorphismus.

virtueller Destruktor

Es ist sicher, den Destruktor der übergeordneten Klasse für die virtuelle Vererbung zu belassen.

virtual ~Character() = default;

Wenn beim Löschen eines untergeordneten Objekts über den übergeordneten Zeiger kein virtueller Destruktor vorhanden ist, wird der untergeordnete Destruktor möglicherweise nicht aufgerufen. Wenn das Kind über Ressourcen wie Dateien oder Speicher verfügt, wird die Bereinigung möglicherweise verpasst.

Objektlebensdauer und RAII

Lokale Objekte werden am Ende eines Blocks automatisch zerstört.

void Battle()
{
    Warrior warrior{"Rose"};
    std::cout << warrior.Attack() << '\n';
} //Hier wird der Destruktor des Kriegers genannt.

Die Methode, die Objekterstellung und Ressourcenbeschaffung sowie Objektzerstörung und Ressourcenfreigabe verknüpft, heißt RAII. Auch wenn mitten in der Funktion eine Rückgabe erfolgt, wird der Destruktor des lokalen Objekts aufgerufen.

Gut. Jetzt lässt sich am Code erkennen, wer wann was aufräumt. Wenn new und delete dagegen überall verstreut sind, ist schwer zu erkennen, wem ein Objekt gehört.

Objekte, die Polymorphismus erfordern, sollten nicht als Werte im übergeordneten Typ gespeichert werden. std::vector<Character>Die Eingabe von „Warrior“ als Wert kann zu Objekt-Slicing führen, bei dem untergeordnete Teile abgeschnitten werden.

Daher muss es wie folgt in Zeigerform gespeichert werden.

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

Jetzt brauchen wir intelligente Zeiger, die es Zeigern ermöglichen, Objekte automatisch zu bereinigen, während sie sie besitzen.