Menu
Coddy logo textTech

Analyse approfondie du destructeur

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 24 sur 104.

Un destructeur est une fonction membre spéciale qui s'exécute automatiquement lorsqu'un objet est détruit. Son rôle principal est de libérer les ressources acquises par l'objet au cours de sa durée de vie, telles que la mémoire allouée dynamiquement, les descripteurs de fichiers ou les connexions réseau.

Le destructeur a le même nom que la classe, précédé d'un tilde ~. Il ne prend aucun paramètre et n'a pas de type de retour :

class FileHandler {
    std::string filename;
    int* buffer;
public:
    FileHandler(std::string name, size_t size) 
        : filename(name), buffer(new int[size]) {
        std::cout << "Opening " << filename << "\n";
    }
    
    ~FileHandler() {
        delete[] buffer;
        std::cout << "Closing " << filename << "\n";
    }
};

Les destructeurs sont appelés automatiquement dans les situations suivantes :

  • Les objets de la pile sortent de la portée
  • delete est appelé sur un objet alloué sur le tas
  • La durée de vie d'un objet temporaire se termine
void example() {
    FileHandler f1("data.txt", 100);    // Constructeur appelé
    
    FileHandler* f2 = new FileHandler("log.txt", 50);
    delete f2;                           // Destructeur appelé pour f2
    
}   // Destructeur appelé pour f1 (sort de la portée)

Contrairement aux constructeurs, une classe ne peut avoir qu'un seul destructeur. Vous ne pouvez pas le surcharger.

Si vous n'en définissez pas un, le compilateur génère un destructeur par défaut qui ne fait rien de particulier - il détruit simplement chaque membre. Pour les classes gérant des ressources, vous devez écrire le vôtre pour éviter les fuites de mémoire.

challenge icon

Défi

Facile

Construisons un gestionnaire de session qui suit les sessions utilisateur actives et démontre comment les destructeurs nettoient automatiquement les ressources lorsque les objets sont détruits — qu'ils sortent de la portée (scope) ou qu'ils soient explicitement supprimés.

Vous allez créer deux fichiers pour organiser votre code :

  • Session.h : Définissez une classe Session qui représente une session utilisateur active avec des données allouées dynamiquement. Votre classe doit avoir :
    • Des membres privés : un username (string), un sessionId (int), et un pointeur vers un tableau d'entiers appelé activityLog qui suit les actions de l'utilisateur, ainsi qu'un logSize pour la taille du tableau.
    • Un constructeur qui prend un nom d'utilisateur, un ID de session et une taille de journal. Il doit allouer le tableau du journal d'activité sur le tas (heap), initialiser tous les éléments à 0, et afficher "Session <sessionId> started for <username>"
    • Un destructeur qui libère la mémoire allouée et affiche "Session <sessionId> ended for <username>"
    • Une méthode logActivity(int activityCode) qui stocke le code d'activité dans le prochain emplacement disponible (suivez la position actuelle en interne).
    • Une méthode getActivityCount() qui retourne le nombre d'activités enregistrées.
    • Une méthode getUsername() qui retourne le nom d'utilisateur.
  • main.cpp : Démontrez le comportement du destructeur dans différents scénarios. Lisez un nom d'utilisateur et une taille de journal depuis l'entrée (chacun sur une ligne séparée), puis :
    • Créez un bloc de portée en utilisant des accolades { } et, à l'intérieur, créez une Session allouée sur la pile (stack) avec le nom d'utilisateur saisi, l'ID de session 101, et la taille de journal saisie. Enregistrez deux activités avec les codes 1 et 2, puis affichez "Stack session activities: <count>". Lorsque le bloc se termine, le destructeur doit s'exécuter automatiquement.
    • Après le bloc, affichez "Stack session destroyed"
    • Créez une Session allouée sur le tas (heap) en utilisant new avec le nom d'utilisateur "Guest", l'ID de session 202, et une taille de journal de 3. Enregistrez une activité avec le code 5, affichez "Heap session activities: <count>", puis supprimez-la explicitement avec delete.
    • Après la suppression, affichez "Heap session destroyed"

Ce défi montre les deux principales façons dont les destructeurs sont appelés : automatiquement lorsque les objets de la pile quittent la portée, et manuellement lorsque vous supprimez des objets du tas. Les deux chemins doivent libérer correctement le journal d'activité alloué dynamiquement pour éviter les fuites de mémoire.

Incluez votre fichier d'en-tête dans main.cpp en utilisant #include "Session.h".

Aide-mémoire

Un destructeur est une fonction membre spéciale qui s'exécute automatiquement lorsqu'un objet est détruit. Il libère des ressources telles que la mémoire allouée dynamiquement, les descripteurs de fichiers ou les connexions réseau.

Le destructeur porte le même nom que la classe, précédé d'un tilde ~. Il ne prend aucun paramètre et n'a pas de type de retour :

class FileHandler {
    std::string filename;
    int* buffer;
public:
    FileHandler(std::string name, size_t size) 
        : filename(name), buffer(new int[size]) {
        std::cout << "Opening " << filename << "\n";
    }
    
    ~FileHandler() {
        delete[] buffer;
        std::cout << "Closing " << filename << "\n";
    }
};

Les destructeurs sont appelés automatiquement lorsque :

  • Les objets de la pile sortent de la portée
  • delete est appelé sur un objet alloué sur le tas
  • La durée de vie d'un objet temporaire se termine
void example() {
    FileHandler f1("data.txt", 100);    // Constructeur appelé
    
    FileHandler* f2 = new FileHandler("log.txt", 50);
    delete f2;                           // Destructeur appelé pour f2
    
}   // Destructeur appelé pour f1 (sort de la portée)

Une classe ne peut avoir qu'un seul destructeur (pas de surcharge). Si vous n'en définissez pas, le compilateur génère un destructeur par défaut. Pour les classes gérant des ressources, vous devez écrire le vôtre pour éviter les fuites de mémoire.

Essayez vous-même

#include <iostream>
#include <string>
#include "Session.h"

using namespace std;

int main() {
    // Lire l'entrée
    string username;
    int logSize;
    getline(cin, username);
    cin >> logSize;
    
    // TODO: Créer un bloc de portée avec des accolades { }
    // À l'intérieur du bloc :
    //   - Créer une Session allouée sur la pile avec le username d'entrée, l'ID de session 101, et la taille de log d'entrée
    //   - Enregistrer deux activités avec les codes 1 et 2
    //   - Afficher "Stack session activities: <count>"
    // Lorsque le bloc se termine, le destructeur s'exécute automatiquement
    
    // TODO: Après le bloc, afficher "Stack session destroyed"
    
    // TODO: Créer une Session allouée sur le tas en utilisant new
    //   - Username: "Guest", ID de session : 202, taille de log : 3
    //   - Enregistrer une activité avec le code 5
    //   - Afficher "Heap session activities: <count>"
    //   - Supprimer la session avec delete
    
    // TODO: Après la suppression, afficher "Heap session destroyed"
    
    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