Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

CSCI 235 Vererbung in C++: Meistere die vier Rollen – Magier, Schurke, Waldläufer, Barbar

Lerne, wie du mit Vererbung in C++ die vier Charakterklassen Magier, Schurke, Waldläufer und Barbar implementierst. Inklusive praktischer Tipps zu Konstruktoren, Settern und Gettern – inspiriert von aktuellen Gaming-Trends.

CSCI 235 Vererbung C++ Vererbung Tutorial Magier Klasse C++ Schurke Klasse C++ Waldläufer Klasse C++ Barbar Klasse C++ OOP Vererbung Beispiel C++ Unterklassen Setter Getter C++ enum in C++ C++ Konstruktor Vererbung RPG Charakterklassen C++ C++ Programmierung Übung CSCI 235 Projekt 2 C++ Validierung Eingabe C++ private Member

Einführung in die Vererbung mit CSCI 235

In der objektorientierten Programmierung (OOP) ist Vererbung ein mächtiges Konzept, das es ermöglicht, neue Klassen auf Basis bestehender zu erstellen. Im Rahmen des Projekts CSCI 235 Algorithmic Adventures: Into the Recursive Realms implementierst du vier Unterklassen der Character-Klasse: Mage, Scoundrel, Ranger und Barbarian. Jede Klasse erbt die gemeinsamen Eigenschaften des Charakters und fügt spezifische Attribute und Methoden hinzu. Dieses Tutorial führt dich Schritt für Schritt durch die Implementierung – mit besonderem Fokus auf die Mage- und Scoundrel-Klassen. Wir nutzen dabei aktuelle Beispiele aus der Gaming-Welt, um die Konzepte greifbar zu machen.

Warum Vererbung? Ein Trend-Beispiel aus der Spieleentwicklung

Stell dir vor, du entwickelst ein Rollenspiel wie Baldur's Gate 3 oder World of Warcraft. Jeder Charakter hat Basisattribute wie Name, Rasse, Lebenspunkte und Rüstung. Aber ein Magier hat andere Fähigkeiten als ein Schurke. Mit Vererbung definierst du einmal die Basisklasse Character und leitest dann spezialisierte Klassen ab. Das spart Code, erleichtert Wartung und macht dein Programm flexibler – genau das, was in modernen Game-Engines wie Unreal Engine oder Unity passiert. Auch in der aktuellen KI-Entwicklung (z. B. bei Chatbots oder NPC-Steuerung) wird Vererbung genutzt, um verschiedene Verhaltenstypen zu modellieren.

Grundlagen der Vererbung in C++

Bevor wir in die Details einsteigen, wiederholen wir die Syntax. Eine abgeleitete Klasse wird mit class Derived : public Base deklariert. Der public-Modus bedeutet, dass öffentliche Member der Basisklasse auch in der abgeleiteten Klasse öffentlich bleiben. Private Member der Basisklasse sind jedoch nicht direkt zugänglich – du brauchst öffentliche Getter/Setter oder protected Member.

class Character {
private:
    std::string name;
    std::string race;
    int vitality;
    int maxArmor;
    int level;
    bool isEnemy;
public:
    // Konstruktoren und Methoden
};

class Mage : public Character {
private:
    std::string schoolOfMagic;
    std::string weapon;
    bool canSummonIncarnate;
public:
    // Mage-spezifische Methoden
};

Die Mage-Klasse implementieren

Die Mage-Klasse repräsentiert einen Gelehrten der Magie. Neben den geerbten Attributen hat sie drei private Member: schoolOfMagic (z. B. ELEMENTAL, NECROMANCY, ILLUSION), weapon (WAND oder STAFF) und canSummonIncarnate (bool). Die Konstruktoren müssen die Basisklasse initialisieren und dann die Mage-spezifischen Werte setzen.

Default Constructor

Der Standardkonstruktor setzt den Namen auf "NAMELESS", alle booleschen Werte auf false, und die Schule sowie Waffe auf "NONE". Rufe den Default-Konstruktor der Basisklasse auf:

Mage::Mage() : Character(), schoolOfMagic("NONE"), weapon("NONE"), canSummonIncarnate(false) {}

Parameterized Constructor

Der parametrisierte Konstruktor übernimmt Werte für alle Attribute. Achte darauf, dass die Schule und Waffe gültig sind – sonst setze sie auf "NONE". Verwende toupper oder eine Hilfsfunktion, um die Eingabe in Großbuchstaben zu konvertieren.

Mage::Mage(const std::string& name, const std::string& race, int vitality, int maxArmor, int level, bool isEnemy,
           const std::string& school, const std::string& weapon, bool canSummon)
    : Character(name, race, vitality, maxArmor, level, isEnemy) {
    setSchool(school);
    setCastingWeapon(weapon);
    setIncarnateSummon(canSummon);
}

Die Methode setSchool prüft, ob der übergebene String (nach Umwandlung in Großbuchstaben) in der Liste ["ELEMENTAL", "NECROMANCY", "ILLUSION"] enthalten ist. Falls nicht, bleibt der Wert unverändert (oder wird auf "NONE" gesetzt – je nach Spezifikation). Hier eine mögliche Implementierung:

bool Mage::setSchool(const std::string& school) {
    std::string upper = school;
    for (auto& c : upper) c = toupper(c);
    if (upper == "ELEMENTAL" || upper == "NECROMANCY" || upper == "ILLUSION") {
        schoolOfMagic = upper;
        return true;
    }
    return false;
}

Analog funktioniert setCastingWeapon für die Waffen [WAND, STAFF]. Die Getter sind einfach: getSchool() gibt schoolOfMagic zurück, getCastingWeapon() gibt weapon zurück. hasIncarnateSummon() gibt den booleschen Wert zurück.

Die Scoundrel-Klasse implementieren

Der Scoundrel (Schurke) ist ein gerissener Charakter mit einem Dolch (Dagger) aus einem bestimmten Material, einer Fraktion (Faction) und der Möglichkeit, eine Verkleidung (disguise) zu besitzen. Die Dolch-Materialien werden als Enum definiert: enum Dagger { WOOD, BRONZE, IRON, STEEL, MITHRIL, ADAMANT, RUNE };. Die Fraktion ist ein String, der nur gültige Werte wie "CUTPURSE", "NIGHTBLADE" oder "SILVERTONGUE" annehmen darf.

Enum und Member-Variablen

class Scoundrel : public Character {
private:
    Dagger dagger;
    std::string faction;
    bool hasDisguise;
public:
    // Methoden
};

Konstruktoren

Der Standardkonstruktor setzt den Dolch auf WOOD, die Fraktion auf "NONE" und hasDisguise auf false. Der parametrisierte Konstruktor erhält den Dolch als String (z. B. "iron") und konvertiert ihn in den Enum. Nutze eine Hilfsfunktion oder eine Map für die Umwandlung:

Scoundrel::Scoundrel(const std::string& name, const std::string& race, int vitality, int maxArmor, int level, bool isEnemy,
                     const std::string& daggerStr, const std::string& faction, bool disguise)
    : Character(name, race, vitality, maxArmor, level, isEnemy) {
    setDagger(daggerStr);
    setFaction(faction);
    setDisguise(disguise);
}

Setter und Getter

setDagger konvertiert den String in Großbuchstaben und sucht nach dem passenden Enum-Wert. Falls ungültig, bleibt der aktuelle Wert erhalten (oder setze auf WOOD). Beispiel:

bool Scoundrel::setDagger(const std::string& daggerStr) {
    std::string upper = daggerStr;
    for (auto& c : upper) c = toupper(c);
    if (upper == "WOOD") dagger = WOOD;
    else if (upper == "BRONZE") dagger = BRONZE;
    else if (upper == "IRON") dagger = IRON;
    else if (upper == "STEEL") dagger = STEEL;
    else if (upper == "MITHRIL") dagger = MITHRIL;
    else if (upper == "ADAMANT") dagger = ADAMANT;
    else if (upper == "RUNE") dagger = RUNE;
    else return false;
    return true;
}

setFaction prüft auf gültige Werte: "CUTPURSE", "NIGHTBLADE", "SILVERTONGUE" (nach Umwandlung in Großbuchstaben). setDisguise setzt einfach den booleschen Wert. Die Getter getDagger (Rückgabe als String oder Enum) und getFaction sind straightforward.

Häufige Fehler und Best Practices

Ein typischer Fehler ist das Vergessen, die Basisklasse im Konstruktor zu initialisieren. Verwende immer die Member-Initialisierungsliste. Achte auch auf die korrekte Groß-/Kleinschreibung bei der Eingabevalidierung. Nutze const-Referenzen für Parameter, um Kopien zu vermeiden. Dokumentiere jeden Methodenkopf mit @param und @return, wie in der Aufgabenstellung gefordert.

Testen der Implementierung

Nachdem du die Klassen geschrieben hast, teste sie mit einfachen main-Funktionen. Erstelle einen Magier mit gültigen Werten und prüfe die Getter. Versuche, eine ungültige Schule zu setzen – die Methode sollte false zurückgeben und den Wert nicht ändern. Verwende Assertions oder gebe die Werte auf der Konsole aus.

int main() {
    Mage mage("Gandalf", "Human", 100, 50, 10, false, "elemental", "staff", true);
    std::cout << mage.getSchool(); // ELEMENTAL
    std::cout << mage.getCastingWeapon(); // STAFF
    std::cout << mage.hasIncarnateSummon(); // 1 (true)
    
    mage.setSchool("divination"); // false, bleibt ELEMENTAL
    return 0;
}

Ausblick: Ranger und Barbarian

Die Prinzipien für Ranger und Barbarian sind ähnlich. Der Ranger hat eine Armbrust mit Munition, verschiedene Pfeiltypen und einen Tiergefährten. Der Barbar kann Zweihandwaffen oder Einhandwaffe mit Schild führen und einen Wutmodus aktivieren. Die Implementierung folgt demselben Muster: private Member, Konstruktoren, Getter/Setter mit Validierung. Denk daran, dass der Barbar eine enrage-Methode haben könnte, die den Schaden verdoppelt – ein schönes Beispiel für polymorphes Verhalten, das du später mit virtuellen Funktionen umsetzen kannst.

Fazit

Vererbung in C++ ist ein zentrales Werkzeug, um komplexe Systeme wie Charakterklassen in Spielen zu modellieren. Mit diesem Tutorial hast du die Grundlagen für die CSCI 235 Aufgabe gelegt. Übe die Implementierung der vier Rollen, achte auf die exakte Einhaltung der Spezifikation (Funktionsnamen, Parameter, Rückgabetypen) und dokumentiere deinen Code gründlich. Viel Erfolg im Algorithmic Adventures-Projekt!