Constructeurs et destructeurs en C++
Apprenez les constructeurs et destructeurs en C++ pour comprendre le cycle de vie des objets et la gestion des ressources.
En C++, chaque classe possède des méthodes spéciales appelées lors de la création et de la destruction d’un objet : le constructeur et le destructeur. Le constructeur initialise l’objet pour sa première utilisation, tandis que le destructeur libère la mémoire. Cette structure constitue la base du principe RAII (Resource Acquisition Is Initialization).
1. Qu’est-ce qu’un constructeur ?
Un constructeur est une méthode spéciale appelée automatiquement lorsqu’un objet d’une classe est créé. Son rôle est d’initialiser les variables et de préparer les ressources nécessaires.
- Son nom doit être identique à celui de la classe.
- Il ne possède pas de type de retour.
- Il peut être surchargé (plusieurs constructeurs dans la même classe).
class Book {
private:
string title;
int pages;
public:
// Constructeur par défaut
Book() {
title = "Inconnu";
pages = 0;
cout << "Constructeur par défaut appelé." << endl;
}
// Constructeur avec paramètres
Book(string t, int p) {
title = t;
pages = p;
cout << "Constructeur paramétré appelé." << endl;
}
void Display() {
cout << "Livre : " << title << " (" << pages << " pages)" << endl;
}
};
Le constructeur est automatiquement exécuté lors de la création de l’objet :
int main() {
Book b1;
Book b2("Programmation C++", 900);
b1.Display();
b2.Display();
}
2. Qu’est-ce qu’un destructeur ?
Un destructeur est une méthode spéciale appelée lorsque la durée de vie d’un objet prend fin. Il sert à effectuer des opérations de nettoyage, comme libérer la mémoire ou fermer des fichiers.
- Son nom est identique à celui de la classe, précédé d’un
~(tilde). - Il ne prend pas de paramètres et ne peut pas être surchargé.
class Book {
public:
Book() {
cout << "Constructeur exécuté." << endl;
}
~Book() {
cout << "Destructeur exécuté." << endl;
}
};
int main() {
Book b;
} // Le destructeur est automatiquement appelé à la fin de main()
Sortie :
Constructeur exécuté.
Destructeur exécuté.
Comme le destructeur est appelé automatiquement, il ne doit pas être invoqué manuellement.
3. Exemple Book : constructeur et destructeur avancés
Rendons maintenant la classe Book plus réaliste. Elle contiendra des données allouées dynamiquement (par exemple, une description du livre). Cela permettra de comprendre l’importance du destructeur.
#include <iostream>
#include <string>
using namespace std;
class Book {
private:
string title;
int pages;
string *description; // Mémoire dynamique
public:
// Constructeur
Book(string t, int p, string d)
: title(t), pages(p) {
description = new string(d);
cout << "Constructeur Book : " << title << endl;
}
// Destructeur
~Book() {
cout << "Destructeur Book : " << title << endl;
delete description; // Libération de la mémoire
}
void Display() const {
cout << "Livre : " << title << " (" << pages << " pages)" << endl;
cout << "Description : " << *description << endl;
}
};
int main() {
Book b1("1984", 328, "Un roman dystopique.");
b1.Display();
}
Sortie :
Constructeur Book : 1984
Livre : 1984 (328 pages)
Description : Un roman dystopique.
Destructeur Book : 1984
Le destructeur est appelé automatiquement à la fin de main(),
et libère la mémoire allouée avec new.
4. Surcharge de constructeurs
Une classe peut avoir plusieurs constructeurs. Le constructeur approprié est appelé selon le nombre et le type des paramètres.
class Book {
private:
string title;
int pages;
public:
Book() : title("Inconnu"), pages(0) {}
Book(string t) : title(t), pages(100) {}
Book(string t, int p) : title(t), pages(p) {}
void Display() {
cout << "Livre : " << title << " (" << pages << " pages)" << endl;
}
};
int main() {
Book b1;
Book b2("C++ 101");
Book b3("Design Patterns", 450);
b1.Display();
b2.Display();
b3.Display();
}
5. Constructeur de copie
Par défaut, C++ crée automatiquement un constructeur de copie, mais si une classe utilise de la mémoire dynamique, il est nécessaire de définir son propre constructeur de copie.
class Book {
private:
string title;
string *desc;
public:
Book(string t, string d) {
title = t;
desc = new string(d);
cout << "Constructeur : " << title << endl;
}
// Constructeur de copie
Book(const Book &other) {
title = other.title;
desc = new string(*other.desc); // copie profonde
cout << "Constructeur de copie : " << title << endl;
}
~Book() {
delete desc;
cout << "Destructeur : " << title << endl;
}
void Display() {
cout << title << " - " << *desc << endl;
}
};
int main() {
Book b1("C++ Primer", "Guide pour débutants");
Book b2 = b1; // le constructeur de copie est appelé
b2.Display();
}
Si une copie profonde n’est pas effectuée, les deux objets partageront la même zone mémoire, provoquant une erreur de “double suppression” lorsque le destructeur est appelé.
6. Système de livre – Suivi du cycle de vie
L’exemple suivant montre l’ordre de création et de destruction d’un objet Book. Il est idéal pour comprendre le véritable cycle de vie des constructeurs et des destructeurs.
#include <iostream>
#include <string>
using namespace std;
class Author {
public:
Author(string n) { cout << "Auteur créé : " << n << endl; }
~Author() { cout << "Auteur détruit." << endl; }
};
class Book {
private:
string title;
Author author;
public:
Book(string t, string a) : title(t), author(a) {
cout << "Livre créé : " << title << endl;
}
~Book() {
cout << "Livre détruit : " << title << endl;
}
};
int main() {
Book b("La Ferme des animaux", "George Orwell");
}
Sortie :
Auteur créé : George Orwell
Livre créé : La Ferme des animaux
Livre détruit : La Ferme des animaux
Auteur détruit.
Comme on peut le voir, le constructeur de Author est appelé en premier,
suivi du constructeur de Book.
Lors de la destruction, l’ordre inverse est appliqué (d’abord Book, puis Author).
Cela montre l’ordre du cycle de vie dans les relations de composition en C++.
7. TL;DR
- Constructeur → Appelé automatiquement lors de la création d’un objet.
- Destructeur → Appelé automatiquement à la fin du cycle de vie de l’objet.
- Les constructeurs peuvent être surchargés, les destructeurs ne le peuvent pas.
- Si une mémoire dynamique est utilisée, le destructeur doit appeler
delete. - Le constructeur de copie doit effectuer une copie profonde.
- Ordre de création : d’abord les composants, puis la classe ; Ordre de destruction : d’abord la classe, puis les composants.
- Tous les exemples peuvent être exécutés dans Visual Studio 2022 ou GCC 11+.