Classes abstraites
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 60 sur 104.
Une classe abstraite est une classe qui contient au moins une fonction virtuelle pure. Comme elle possède une interface incomplète, vous ne pouvez pas créer d'objets d'une classe abstraite directement - elle existe uniquement pour qu'on en hérite.
class Shape {
public:
virtual double area() = 0; // Virtuelle pure - rend Shape abstraite
virtual double perimeter() = 0; // Une autre fonction virtuelle pure
void printInfo() { // Méthode régulière - possède une implémentation
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default;
};
Shape s; // Erreur : impossible d'instancier une classe abstraiteLes classes abstraites peuvent contenir un mélange de fonctions virtuelles pures (aucune implémentation), de fonctions virtuelles régulières (avec une implémentation par défaut) et de fonctions non virtuelles. Cela vous permet de définir un comportement commun tout en exigeant que les classes dérivées implémentent des fonctionnalités spécifiques.
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 implémente toutes les fonctions virtuelles pures
c.printInfo(); // Utilise printInfo() héritée, appelle area() de CircleUne classe dérivée doit implémenter toutes les fonctions virtuelles pures pour devenir concrète (instanciable). Si elle en laisse certaines non implémentées, elle reste elle-même abstraite. Ce mécanisme garantit que chaque classe concrète de votre hiérarchie fournit la fonctionnalité requise.
Défi
FacileConstruisons un système de lecteur multimédia qui démontre comment les classes abstraites définissent des contrats pour différents types de médias. Vous allez créer une classe de base abstraite qui établit ce que chaque lecteur multimédia doit faire, puis implémenter des lecteurs concrets pour le contenu audio et vidéo.
Vous organiserez votre code sur trois fichiers :
MediaPlayer.h: Définissez une classe abstraiteMediaPlayerqui sert de modèle pour tous les types de médias. Votre classe abstraite doit avoir :- Un membre protégé
std::string title - Un membre protégé
int duration(en secondes) - Un constructeur qui initialise les deux valeurs
- Une méthode virtuelle pure
play()— chaque type de média sera lu différemment - Une méthode virtuelle pure
getMediaType()retournant unstd::string - Une méthode régulière (non virtuelle)
showInfo()qui affiche :<title> (<duration>s) - Type: <mediaType>— cette méthode doit appelergetMediaType()pour obtenir le type - Un destructeur virtuel
- Un membre protégé
Players.h: Implémentez deux lecteurs multimédias concrets qui héritent deMediaPlayer:AudioPlayer:- Un membre privé
std::string artist - Un constructeur prenant le titre, la durée et l'artiste
- Implémentez
play()pour afficher :Playing audio: <title> by <artist> - Implémentez
getMediaType()pour retourner"Audio"
VideoPlayer:- Un membre privé
std::string resolution - Un constructeur prenant le titre, la durée et la résolution
- Implémentez
play()pour afficher :Playing video: <title> in <resolution> - Implémentez
getMediaType()pour retourner"Video"
- Un membre privé
main.cpp: Lisez quatre entrées (chacune sur une ligne séparée) :- Le titre de la piste audio
- La durée de l'audio (entier)
- Le titre de la vidéo
- La durée de la vidéo (entier)
Créez un
AudioPlayeravec l'artiste"Unknown Artist"et unVideoPlayeravec la résolution"1080p". Stockez les deux dans un tableau de pointeursMediaPlayer*.Parcourez le tableau et pour chaque lecteur, appelez d'abord
showInfo(), puis appelezplay(). Affichez une ligne vide entre chaque élément multimédia. Nettoyez vos objets alloués dynamiquement une fois terminé.
Par exemple, avec les entrées Summer Vibes, 180, Nature Documentary, et 3600 :
Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist
Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080pRemarquez comment la méthode showInfo() est définie une seule fois dans la classe de base abstraite mais appelle la méthode virtuelle pure getMediaType() — cela démontre comment les classes abstraites peuvent mélanger des implémentations concrètes avec des méthodes que les classes dérivées doivent fournir. N'oubliez pas d'utiliser le mot-clé override sur toutes les méthodes surchargées.
Aide-mémoire
Une classe abstraite contient au moins une fonction virtuelle pure et ne peut pas être instanciée directement. Elle existe pour qu'on en hérite et pour définir une interface pour les classes dérivées.
Définir une classe abstraite
Utilisez = 0 pour déclarer une fonction virtuelle pure :
class Shape {
public:
virtual double area() = 0; // Fonction virtuelle pure
virtual double perimeter() = 0; // Rend Shape abstraite
void printInfo() { // Méthode régulière avec implémentation
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default; // Destructeur virtuel
};Les classes abstraites peuvent contenir :
- Des fonctions virtuelles pures (sans implémentation)
- Des fonctions virtuelles régulières (avec une implémentation par défaut)
- Des fonctions non virtuelles
Implémenter des classes concrètes
Une classe dérivée doit implémenter toutes les fonctions virtuelles pures pour pouvoir être instanciée :
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 : toutes les fonctions virtuelles pures sont implémentées
c.printInfo(); // La méthode héritée appelle area() de CircleSi une classe dérivée n'implémente pas toutes les fonctions virtuelles pures, elle reste abstraite et ne peut pas être instanciée.
Essayez vous-même
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"
int main() {
// Lire les entrées
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: Créer un AudioPlayer avec l'artiste "Unknown Artist"
// TODO: Créer un VideoPlayer avec la résolution "1080p"
// TODO: Stocker les deux dans un tableau de pointeurs MediaPlayer*
// TODO: Parcourir le tableau et pour chaque lecteur :
// 1. Appeler showInfo()
// 2. Appeler play()
// 3. Afficher une ligne vide entre chaque élément multimédia
// TODO: Libérer les objets alloués dynamiquement
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d'accès en C++Spécificateurs d'accès en profondeurMasquage d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l'opérateur d'affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif - Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique