Menu
Coddy logo textTech

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 abstraite

Les 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 Circle

Une 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.

challenge icon

Défi

Facile

Construisons 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 abstraite MediaPlayer qui 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 un std::string
    • Une méthode régulière (non virtuelle) showInfo() qui affiche : <title> (<duration>s) - Type: <mediaType> — cette méthode doit appeler getMediaType() pour obtenir le type
    • Un destructeur virtuel
  • Players.h : Implémentez deux lecteurs multimédias concrets qui héritent de MediaPlayer :

    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"
  • main.cpp : Lisez quatre entrées (chacune sur une ligne séparée) :
    1. Le titre de la piste audio
    2. La durée de l'audio (entier)
    3. Le titre de la vidéo
    4. La durée de la vidéo (entier)

    Créez un AudioPlayer avec l'artiste "Unknown Artist" et un VideoPlayer avec la résolution "1080p". Stockez les deux dans un tableau de pointeurs MediaPlayer*.

    Parcourez le tableau et pour chaque lecteur, appelez d'abord showInfo(), puis appelez play(). 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 1080p

Remarquez 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 Circle

Si 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;
}
quiz iconTestez-vous

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