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 werdenAbstrakte 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 aufEine 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.
Aufgabe
EinfachLassen 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 KlasseMediaPlayer, die als Blaupause für alle Medientypen dient. Ihre abstrakte Klasse sollte Folgendes enthalten:- Ein geschütztes (protected)
std::string titleMitglied - Ein geschütztes (protected)
int durationMitglied (in Sekunden) - Einen Konstruktor, der beide Werte initialisiert
- Eine rein virtuelle
play()Methode — jeder Medientyp wird unterschiedlich abgespielt - Eine rein virtuelle
getMediaType()Methode, die einenstd::stringzurückgibt - Eine reguläre (nicht-virtuelle)
showInfo()Methode, die Folgendes ausgibt:<title> (<duration>s) - Type: <mediaType>— diese Methode solltegetMediaType()aufrufen, um den Typ zu erhalten - Einen virtuellen Destruktor
- Ein geschütztes (protected)
Players.h: Implementieren Sie zwei konkrete Mediaplayer, die vonMediaPlayererben:AudioPlayer:- Ein privates
std::string artistMitglied - 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 resolutionMitglied - 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
- Ein privates
main.cpp: Lesen Sie vier Eingaben ein (jede in einer separaten Zeile):- Titel des Audiotracks
- Audio-Dauer (Ganzzahl)
- Video-Titel
- Video-Dauer (Ganzzahl)
Erstellen Sie einen
AudioPlayermit dem Künstler"Unknown Artist"und einenVideoPlayermit der Auflösung"1080p". Speichern Sie beide in einem Array vonMediaPlayer*Pointern.Iterieren Sie durch das Array und rufen Sie für jeden Player zuerst
showInfo()und dannplay()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 1080pBeachten 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 aufWenn 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;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container