Menu
Coddy logo textTech

Abstrakte Klassen

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 60 von 104.

Eine abstrakte Klasse ist jede Klasse, die mindestens eine rein virtuelle Funktion enthält. Da sie eine unvollständige Schnittstelle hat, können Sie keine Objekte einer abstrakten Klasse direkt erstellen – sie existiert ausschließlich dazu, um von ihr zu erben.

class Shape {
public:
    virtual double area() = 0;      // Rein virtuell - macht Shape abstrakt
    virtual double perimeter() = 0; // Eine weitere rein virtuelle Funktion
    
    void printInfo() {              // Reguläre Methode - hat eine Implementierung
        std::cout << "Area: " << area() << std::endl;
    }
    
    virtual ~Shape() = default;
};

Shape s;  // Fehler: Abstrakte Klasse kann nicht instanziiert werden

Abstrakte Klassen können eine Mischung aus rein virtuellen Funktionen (ohne Implementierung), regulären virtuellen Funktionen (mit Standardimplementierung) und nicht-virtuellen Funktionen enthalten. Dies ermöglicht es Ihnen, gemeinsames Verhalten zu definieren, während abgeleitete Klassen verpflichtet werden, spezifische Funktionalitäten zu implementieren.

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
};

Circle c(5.0);      // OK: Circle implementiert alle rein virtuellen Funktionen
c.printInfo();      // Verwendet das geerbte printInfo(), ruft die area() von Circle auf

Eine abgeleitete Klasse muss alle rein virtuellen Funktionen implementieren, um konkret (instanziierbar) zu werden. Wenn sie eine davon nicht implementiert lässt, bleibt sie selbst abstrakt. Dieser Mechanismus stellt sicher, dass jede konkrete Klasse in Ihrer Hierarchie die erforderliche Funktionalität bereitstellt.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Mediaplayer-System bauen, das demonstriert, wie abstrakte Klassen Verträge für verschiedene Medientypen definieren. Sie werden eine abstrakte Basisklasse erstellen, die festlegt, was jeder Mediaplayer tun muss, und dann konkrete Player für Audio- und Videoinhalte implementieren.

Sie werden Ihren Code über drei Dateien organisieren:

  • MediaPlayer.h: Definieren Sie eine abstrakte Klasse MediaPlayer, die als Blaupause für alle Medientypen dient. Ihre abstrakte Klasse sollte Folgendes enthalten:
    • Ein geschütztes (protected) std::string title Mitglied
    • Ein geschütztes (protected) int duration Mitglied (in Sekunden)
    • Einen Konstruktor, der beide Werte initialisiert
    • Eine rein virtuelle play() Methode — jeder Medientyp wird unterschiedlich abgespielt
    • Eine rein virtuelle getMediaType() Methode, die einen std::string zurückgibt
    • Eine reguläre (nicht-virtuelle) showInfo() Methode, die Folgendes ausgibt: <title> (<duration>s) - Type: <mediaType> — diese Methode sollte getMediaType() aufrufen, um den Typ zu erhalten
    • Einen virtuellen Destruktor
  • Players.h: Implementieren Sie zwei konkrete Mediaplayer, die von MediaPlayer erben:

    AudioPlayer:

    • Ein privates std::string artist Mitglied
    • Einen Konstruktor, der title, duration und artist entgegennimmt
    • Implementieren Sie play(), um Folgendes auszugeben: Playing audio: <title> by <artist>
    • Implementieren Sie getMediaType(), um "Audio" zurückzugeben

    VideoPlayer:

    • Ein privates std::string resolution Mitglied
    • Einen Konstruktor, der title, duration und resolution entgegennimmt
    • Implementieren Sie play(), um Folgendes auszugeben: Playing video: <title> in <resolution>
    • Implementieren Sie getMediaType(), um "Video" zurückzugeben
  • main.cpp: Lesen Sie vier Eingaben ein (jede in einer separaten Zeile):
    1. Titel des Audiotracks
    2. Audio-Dauer (Ganzzahl)
    3. Video-Titel
    4. Video-Dauer (Ganzzahl)

    Erstellen Sie einen AudioPlayer mit dem Künstler "Unknown Artist" und einen VideoPlayer mit der Auflösung "1080p". Speichern Sie beide in einem Array von MediaPlayer* Pointern.

    Iterieren Sie durch das Array und rufen Sie für jeden Player zuerst showInfo() und dann play() auf. Geben Sie eine Leerzeile zwischen den einzelnen Medienelementen aus. Bereinigen Sie Ihre dynamisch zugewiesenen Objekte, wenn Sie fertig sind.

Zum Beispiel mit den Eingaben Summer Vibes, 180, Nature Documentary und 3600:

Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist

Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080p

Beachten Sie, wie die Methode showInfo() einmal in der abstrakten Basisklasse definiert ist, aber die rein virtuelle Methode getMediaType() aufruft — dies zeigt, wie abstrakte Klassen konkrete Implementierungen mit Methoden mischen können, die abgeleitete Klassen bereitstellen müssen. Denken Sie daran, das Schlüsselwort override bei allen überschriebenen Methoden zu verwenden.

Spickzettel

Eine abstrakte Klasse enthält mindestens eine rein virtuelle Funktion und kann nicht direkt instanziiert werden. Sie existiert, um von ihr zu erben und eine Schnittstelle für abgeleitete Klassen zu definieren.

Definieren einer abstrakten Klasse

Verwenden Sie = 0, um eine rein virtuelle Funktion zu deklarieren:

class Shape {
public:
    virtual double area() = 0;      // Rein virtuelle Funktion
    virtual double perimeter() = 0; // Macht Shape abstrakt
    
    void printInfo() {              // Reguläre Methode mit Implementierung
        std::cout << "Area: " << area() << std::endl;
    }
    
    virtual ~Shape() = default;     // Virtueller Destruktor
};

Abstrakte Klassen können Folgendes enthalten:

  • Rein virtuelle Funktionen (keine Implementierung)
  • Reguläre virtuelle Funktionen (mit Standardimplementierung)
  • Nicht-virtuelle Funktionen

Implementieren konkreter Klassen

Eine abgeleitete Klasse muss alle rein virtuellen Funktionen implementieren, um instanziierbar zu werden:

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
};

Circle c(5.0);      // OK: Alle rein virtuellen Funktionen implementiert
c.printInfo();      // Geerbte Methode ruft area() von Circle auf

Wenn eine abgeleitete Klasse nicht alle rein virtuellen Funktionen implementiert, bleibt sie abstrakt und kann nicht instanziiert werden.

Probier es selbst

#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"

int main() {
    // Eingaben lesen
    std::string audioTitle;
    int audioDuration;
    std::string videoTitle;
    int videoDuration;
    
    std::getline(std::cin, audioTitle);
    std::cin >> audioDuration;
    std::cin.ignore();
    std::getline(std::cin, videoTitle);
    std::cin >> videoDuration;
    
    // TODO: Erstelle einen AudioPlayer mit dem Künstler "Unknown Artist"
    
    // TODO: Erstelle einen VideoPlayer mit der Auflösung "1080p"
    
    // TODO: Speichere beide in einem Array von MediaPlayer* Pointern
    
    // TODO: Durchlaufe das Array in einer Schleife und für jeden Player:
    //       1. Rufe showInfo() auf
    //       2. Rufe play() auf
    //       3. Gib eine Leerzeile zwischen jedem Medienelement aus
    
    // TODO: Bereinige dynamisch zugewiesene Objekte
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung