Classi astratte
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 60 di 104.
Una classe astratta è una classe che contiene almeno una funzione virtuale pura. Poiché ha un'interfaccia incompleta, non puoi creare direttamente oggetti di una classe astratta: esiste esclusivamente per essere ereditata.
class Shape {
public:
virtual double area() = 0; // Puramente virtuale - rende Shape astratta
virtual double perimeter() = 0; // Un'altra funzione puramente virtuale
void printInfo() { // Metodo ordinario - ha un'implementazione
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default;
};
Shape s; // Errore: impossibile creare un'istanza di una classe astrattaLe classi astratte possono contenere una combinazione di funzioni virtuali pure (senza implementazione), funzioni virtuali normali (con implementazione predefinita) e funzioni non virtuali. Questo ti permette di definire comportamenti comuni, richiedendo al contempo alle classi derivate di implementare funzionalità specifiche.
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 implementa tutte le funzioni virtuali pure
c.printInfo(); // Usa la funzione ereditata printInfo(), chiama area() di CircleUna classe derivata deve implementare tutte le funzioni virtuali pure per diventare concreta (istanziabile). Se ne lascia qualcuna non implementata, rimane astratta. Questo meccanismo garantisce che ogni classe concreta nella tua gerarchia fornisca le funzionalità richieste.
Sfida
FacileCreiamo un sistema di riproduzione multimediale che mostra come le classi astratte definiscono i contratti per diversi tipi di contenuti multimediali. Creerai una classe base astratta che stabilisce cosa deve fare ogni lettore multimediale, quindi implementerai lettori concreti per contenuti audio e video.
Organizzerai il codice in tre file:
MediaPlayer.h: definisci una classe astrattaMediaPlayerche funge da modello per tutti i tipi di contenuti multimediali. La classe astratta deve avere:- Un membro protetto
std::string title - Un membro protetto
int duration(in secondi) - Un costruttore che inizializza entrambi i valori
- Un metodo virtuale puro
play(): ogni tipo di contenuto multimediale verrà riprodotto in modo diverso - Un metodo virtuale puro
getMediaType()che restituisce unstd::string - Un metodo normale (non virtuale)
showInfo()che stampa:<title> (<duration>s) - Type: <mediaType>. Questo metodo deve chiamaregetMediaType()per ottenere il tipo - Un distruttore virtuale
- Un membro protetto
Players.h: implementa due lettori multimediali concreti che ereditano daMediaPlayer:AudioPlayer:- Un membro privato
std::string artist - Un costruttore che accetta titolo, durata e artista
- Implementa
play()per stampare:Playing audio: <title> by <artist> - Implementa
getMediaType()per restituire"Audio"
VideoPlayer:- Un membro privato
std::string resolution - Un costruttore che accetta titolo, durata e risoluzione
- Implementa
play()per stampare:Playing video: <title> in <resolution> - Implementa
getMediaType()per restituire"Video"
- Un membro privato
main.cpp: leggi quattro input (ciascuno su una riga separata):- Titolo della traccia audio
- Durata dell'audio (intero)
- Titolo del video
- Durata del video (intero)
Crea un
AudioPlayercon artista"Unknown Artist"e unVideoPlayercon risoluzione"1080p". Memorizzali entrambi in un array di puntatoriMediaPlayer*.Scorri l'array e, per ogni lettore, chiama prima
showInfo(), poi chiamaplay(). Stampa una riga vuota tra ogni contenuto multimediale. Al termine, libera gli oggetti allocati dinamicamente.
Per esempio, con gli input Summer Vibes, 180, Nature Documentary e 3600:
Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist
Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080pNota come il metodo showInfo() è definito una sola volta nella classe base astratta, ma chiama il metodo virtuale puro getMediaType(). Questo dimostra come le classi astratte possano combinare implementazioni concrete con metodi che le classi derivate devono fornire. Ricordati di usare la parola chiave override per tutti i metodi sovrascritti.
Provalo tu
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"
int main() {
// Leggi i dati in ingresso
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: Crea un AudioPlayer con l'artista "Unknown Artist"
// TODO: Crea un VideoPlayer con risoluzione "1080p"
// TODO: Memorizza entrambi in un array di puntatori MediaPlayer*
// TODO: Scorri l'array e, per ogni player:
// 1. Chiama showInfo()
// 2. Chiama play()
// 3. Stampa una riga vuota tra un elemento multimediale e l'altro
// TODO: Libera gli oggetti allocati dinamicamente
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online