Approfondissement des destructeurs
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 que l’objet a acquises 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 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 dans les situations suivantes :
- Les objets de la pile sortent de leur portée
deleteest appelé sur un objet alloué sur le tas- La durée de vie d’un objet temporaire prend fin
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 destructor par défaut qui ne fait rien de spécial : il détruit simplement chaque membre. Pour les classes qui gèrent des ressources, vous devez écrire votre propre destructor afin d’éviter les fuites de mémoire.
Défi
FacileConstruisons un gestionnaire de sessions qui suit les sessions utilisateur actives et montre comment les destructeurs nettoient automatiquement les ressources lorsque les objets sont détruits, qu’ils sortent de leur portée ou qu’ils soient explicitement supprimés.
Vous allez créer deux fichiers pour organiser votre code :
Session.h: définissez une classeSessionqui représente une session utilisateur active avec des données allouées dynamiquement. Votre classe doit comporter :- Membres privés : un
username(chaîne de caractères), unsessionId(entier) et un pointeur vers un tableau d’entiers appeléactivityLogqui suit les actions de l’utilisateur, ainsi qu’unlogSizeindiquant la taille du tableau - Un constructeur qui reçoit un nom d’utilisateur, un identifiant de session et une taille de journal. Il doit allouer le tableau du journal d’activités sur le tas, 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 l’emplacement disponible suivant (suivez la position actuelle en interne) - Une méthode
getActivityCount()qui renvoie le nombre d’activités enregistrées - Une méthode
getUsername()qui renvoie le nom d’utilisateur
- Membres privés : un
main.cpp: démontrez le comportement des destructeurs dans différents scénarios. Lisez un nom d’utilisateur et une taille de journal depuis l’entrée (chacun sur une ligne distincte), puis :- Créez un bloc de portée à l’aide d’accolades
{ }et, à l’intérieur, créez unSessionalloué sur la pile avec le nom d’utilisateur saisi, l’identifiant de session101et la taille de journal saisie. Enregistrez deux activités avec les codes1et2, 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 un
Sessionalloué sur le tas à l’aide denew, avec le nom d’utilisateur"Guest", l’identifiant de session202et une taille de journal de3. Enregistrez une activité avec le code5, affichez"Heap session activities: <count>", puis utilisez explicitementdelete. - Après la suppression, affichez
"Heap session destroyed"
- Créez un bloc de portée à l’aide d’accolades
Ce défi montre les deux principales façons dont les destructeurs sont appelés : automatiquement lorsque les objets sur la pile sortent de leur portée, et manuellement lorsque vous supprimez des objets du tas. Dans les deux cas, le journal d’activités alloué dynamiquement doit être correctement libéré afin d’éviter les fuites de mémoire.
Incluez votre fichier d’en-tête dans main.cpp à l’aide de #include "Session.h".
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 nom d'utilisateur en entrée, l'ID de session 101 et la taille de journal en entrée
// - Enregistrer deux activités avec les codes 1 et 2
// - Print "Stack session activities: <count>"
// Lorsque le bloc se termine, le destructeur s'exécute automatiquement
// TODO: After the block, print "Stack session destroyed"
// TODO: Créer une Session allouée sur le tas en utilisant new
// - Nom d'utilisateur : "Guest", ID de session : 202, taille de journal : 3
// - Enregistrer une activité avec le code 5
// - Print "Heap session activities: <count>"
// - Supprimer la session
// TODO: After deletion, print "Heap session destroyed"
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 externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents 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 de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des 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 des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne