Klasy abstrakcyjne
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 60 z 104.
Klasa abstrakcyjna to każda klasa zawierająca co najmniej jedną czysto wirtualną funkcję. Ponieważ ma niekompletny interfejs, nie możesz bezpośrednio tworzyć obiektów klasy abstrakcyjnej — istnieje ona wyłącznie po to, by po niej dziedziczyć.
class Shape {
public:
virtual double area() = 0; // Czysto wirtualna — sprawia, że Shape jest klasą abstrakcyjną
virtual double perimeter() = 0; // Kolejna funkcja czysto wirtualna
void printInfo() { // Zwykła metoda — ma implementację
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default;
};
Shape s; // Błąd: nie można utworzyć instancji klasy abstrakcyjnejKlasy abstrakcyjne mogą zawierać funkcje czysto wirtualne (bez implementacji), zwykłe funkcje wirtualne (z domyślną implementacją) oraz funkcje niewirtualne. Dzięki temu możesz definiować wspólne zachowanie, jednocześnie wymagając od klas pochodnych implementacji określonych funkcjonalności.
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 implementuje wszystkie czysto wirtualne funkcje
c.printInfo(); // Używa odziedziczonej funkcji printInfo(), wywołuje funkcję area() klasy CircleKlasa pochodna musi implementować wszystkie czysto wirtualne funkcje, aby stać się klasą konkretną (możliwą do utworzenia instancji). Jeśli pozostawi którąkolwiek z nich niezaimplementowaną, sama pozostanie abstrakcyjna. Ten mechanizm gwarantuje, że każda klasa konkretna w hierarchii zapewnia wymaganą funkcjonalność.
Wyzwanie
ŁatwyZbudujmy system odtwarzacza multimediów, który pokazuje, jak klasy abstrakcyjne definiują kontrakty dla różnych typów mediów. Utworzysz abstrakcyjną klasę bazową, która określa, co musi robić każdy odtwarzacz multimediów, a następnie zaimplementujesz konkretne odtwarzacze treści audio i wideo.
Rozmieścisz swój kod w trzech plikach:
MediaPlayer.h: Zdefiniuj abstrakcyjną klasęMediaPlayer, która będzie wzorcem dla wszystkich typów mediów. Twoja klasa abstrakcyjna powinna mieć:- Chronione pole
std::string title - Chronione pole
int duration(w sekundach) - Konstruktor inicjalizujący obie wartości
- Czysto wirtualną metodę
play(): każdy typ mediów będzie odtwarzany inaczej - Czysto wirtualną metodę
getMediaType(), która zwracastd::string - Zwykłą (niewirtualną) metodę
showInfo(), która wyświetla:<title> (<duration>s) - Type: <mediaType>. Ta metoda powinna wywoływaćgetMediaType(), aby uzyskać typ - Wirtualny destruktor
- Chronione pole
Players.h: Zaimplementuj dwa konkretne odtwarzacze multimediów dziedziczące poMediaPlayer:AudioPlayer:- Prywatne pole
std::string artist - Konstruktor przyjmujący tytuł, czas trwania i wykonawcę
- Zaimplementuj
play(), aby wyświetlała:Playing audio: <title> by <artist> - Zaimplementuj
getMediaType(), aby zwracała"Audio"
VideoPlayer:- Prywatne pole
std::string resolution - Konstruktor przyjmujący tytuł, czas trwania i rozdzielczość
- Zaimplementuj
play(), aby wyświetlała:Playing video: <title> in <resolution> - Zaimplementuj
getMediaType(), aby zwracała"Video"
- Prywatne pole
main.cpp: Odczytaj cztery dane wejściowe (każdą w osobnym wierszu):- Tytuł utworu audio
- Czas trwania audio (liczba całkowita)
- Tytuł wideo
- Czas trwania wideo (liczba całkowita)
Utwórz obiekt
AudioPlayerz wykonawcą"Unknown Artist"oraz obiektVideoPlayerz rozdzielczością"1080p". Zapisz oba w tablicy wskaźnikówMediaPlayer*.Przejdź pętlą przez tablicę i dla każdego odtwarzacza najpierw wywołaj
showInfo(), a następnieplay(). Wyświetl pusty wiersz między poszczególnymi elementami multimedialnymi. Po zakończeniu zwolnij dynamicznie zaalokowane obiekty.
Na przykład dla danych wejściowych Summer Vibes, 180, Nature Documentary i 3600:
Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist
Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080pZauważ, że metoda showInfo() jest zdefiniowana raz w abstrakcyjnej klasie bazowej, ale wywołuje czysto wirtualną metodę getMediaType(). Pokazuje to, że klasy abstrakcyjne mogą łączyć konkretne implementacje z metodami, które muszą dostarczyć klasy pochodne. Pamiętaj, aby użyć słowa kluczowego override we wszystkich nadpisywanych metodach.
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"
int main() {
// Wczytaj dane wejściowe
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: Utwórz AudioPlayer z wykonawcą "Unknown Artist"
// TODO: Utwórz VideoPlayer o rozdzielczości "1080p"
// TODO: Umieść oba obiekty w tablicy wskaźników MediaPlayer*
// TODO: Przejdź przez tablicę i dla każdego odtwarzacza:
// 1. Wywołaj showInfo()
// 2. Wywołaj play()
// 3. Wypisz pustą linię między elementami multimedialnymi
// TODO: Zwolnij dynamicznie zaalokowane obiekty
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online