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 über eine unvollständige Schnittstelle verfügt, kannst du keine Objekte einer abstrakten Klasse direkt erstellen – sie existiert ausschließlich dazu, 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 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. Dadurch kannst du gemeinsames Verhalten definieren und gleichzeitig von abgeleiteten Klassen verlangen, bestimmte Funktionalität 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 die geerbte printInfo(), ruft Circle's area() aufEine abgeleitete Klasse muss alle rein virtuellen Funktionen implementieren, um konkret (instanziierbar) zu werden. Wenn sie eine davon nicht implementiert, bleibt sie selbst abstrakt. Dieser Mechanismus stellt sicher, dass jede konkrete Klasse in deiner Hierarchie die erforderliche Funktionalität bereitstellt.
Aufgabe
EinfachErstellen wir ein Mediaplayer-System, das zeigt, wie abstrakte Klassen Verträge für verschiedene Medientypen definieren. Du erstellst eine abstrakte Basisklasse, die festlegt, was jeder Mediaplayer tun muss, und implementierst anschließend konkrete Player für Audio- und Videoinhalte.
Du wirst deinen Code auf drei Dateien verteilen:
MediaPlayer.h: Definiere eine abstrakte KlasseMediaPlayer, die als Vorlage für alle Medientypen dient. Deine abstrakte Klasse sollte Folgendes enthalten:- Ein geschütztes
std::string title-Member - Ein geschütztes
int duration-Member (in Sekunden) - Einen Konstruktor, der beide Werte initialisiert
- Eine rein virtuelle
play()-Methode: Jeder Medientyp wird anders 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 abzurufen - Einen virtuellen Destruktor
- Ein geschütztes
Players.h: Implementiere zwei konkrete Mediaplayer, die vonMediaPlayererben:AudioPlayer:- Ein privates
std::string artist-Member - Einen Konstruktor, der title, duration und artist entgegennimmt
- Implementiere
play(), sodass Folgendes ausgegeben wird:Playing audio: <title> by <artist> - Implementiere
getMediaType(), sodass"Audio"zurückgegeben wird
VideoPlayer:- Ein privates
std::string resolution-Member - Einen Konstruktor, der title, duration und resolution entgegennimmt
- Implementiere
play(), sodass Folgendes ausgegeben wird:Playing video: <title> in <resolution> - Implementiere
getMediaType(), sodass"Video"zurückgegeben wird
- Ein privates
main.cpp: Lies vier Eingaben ein (jeweils in einer eigenen Zeile):- Titel des Audiotitels
- Audiodauer (Ganzzahl)
- Videotitel
- Videodauer (Ganzzahl)
Erstelle einen
AudioPlayermit dem Künstler"Unknown Artist"und einenVideoPlayermit der Auflösung"1080p". Speichere beide in einem Array ausMediaPlayer*-Zeigern.Durchlaufe das Array und rufe für jeden Player zuerst
showInfo()und anschließendplay()auf. Gib zwischen den einzelnen Medienelementen eine Leerzeile aus. Räume die dynamisch angelegten Objekte anschließend auf.
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 1080pBeachte, dass die Methode showInfo() einmal in der abstrakten Basisklasse definiert wird, aber die rein virtuelle Methode getMediaType() aufruft. Dies zeigt, wie abstrakte Klassen konkrete Implementierungen mit Methoden kombinieren können, die von abgeleiteten Klassen bereitgestellt werden müssen. Denke daran, bei allen überschriebenen Methoden das Schlüsselwort override zu verwenden.
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 Künstler "Unknown Artist"
// TODO: Erstelle einen VideoPlayer mit Auflösung "1080p"
// TODO: Speichere beide in einem Array von MediaPlayer*-Zeigern
// TODO: Durchlaufe das Array und für jeden Player:
// 1. Rufe showInfo() auf
// 2. Rufe play() auf
// 3. Gib eine leere Zeile zwischen jedem Medienelement aus
// TODO: Räume dynamisch allozierte Objekte auf
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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler