RAII en C++
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 16 sur 104.
RAII (Resource Acquisition Is Initialization) est un idiome fondamental de C++ selon lequel la gestion des ressources est liée à la durée de vie de l’objet. L’idée est simple : acquérir les ressources dans le constructeur et les libérer dans le destructeur.
Lorsqu’un objet est créé, il acquiert toutes les ressources dont il a besoin. Lorsque l’objet sort de sa portée, son destructeur libère automatiquement ces ressources. Cela garantit le nettoyage, même si des exceptions surviennent.
class FileHandler {
std::FILE* file;
public:
FileHandler(const char* filename) {
file = std::fopen(filename, "r"); // Acquérir la ressource
}
~FileHandler() {
if (file) std::fclose(file); // Libérer la ressource
}
};Avec RAII, vous n’oubliez jamais de nettoyer :
void processFile() {
FileHandler handler("data.txt"); // Ressource acquise
// Travail avec le fichier...
// Si une exception est levée ici, le destructor s'exécute quand même !
} // Ressource libérée automatiquementLes pointeurs intelligents sont l’exemple parfait de la RAII en action. Un unique_ptr acquiert la propriété de la mémoire du tas dans son constructeur et la supprime dans son destructeur. Vous utilisiez déjà la RAII sans vous en rendre compte !
RAII s’applique à toutes les ressources : mémoire, descripteurs de fichiers, connexions réseau, mutex ou connexions à des bases de données. En encapsulant les ressources dans des objets, C++ garantit un nettoyage déterministe et élimine des catégories entières de bogues, comme les fuites et les descripteurs pendants.
Défi
FacileConstruisons un gestionnaire de connexions qui illustre le principe RAII en gérant automatiquement le cycle de vie de connexions de type réseau. Lorsqu’une connexion est créée, elle doit « s’ouvrir », et lorsqu’elle sort de sa portée, elle doit automatiquement « se fermer » : aucun nettoyage manuel n’est requis !
Vous allez créer deux fichiers pour organiser votre code :
Connection.h: définissez une classeConnectionqui gère une ressource de connexion à l’aide des principes RAII. La classe doit comporter :- Un attribut privé
connectionId(chaîne de caractères) pour identifier la connexion - Un attribut privé
isOpen(booléen) pour suivre l’état de la connexion - Un constructeur qui reçoit un identifiant de connexion, définit
isOpensur true et affiche"Opening connection: <id>" - Un destructeur qui vérifie si la connexion est ouverte, la ferme en définissant
isOpensur false, et affiche"Closing connection: <id>" - Une méthode
send(string message)qui affiche"[<id>] Sending: <message>"si la connexion est ouverte - Une méthode
getId()qui renvoie l’identifiant de la connexion
- Un attribut privé
main.cpp: montrez comment RAII garantit le nettoyage automatique en lisant un identifiant de connexion et un message depuis l’entrée. Ensuite :- Créez une portée à l’aide d’accolades
{ } - À l’intérieur de cette portée, créez sur la pile un objet
Connectionavec l’identifiant saisi - Utilisez la connexion pour envoyer le message saisi
- Laissez la portée se terminer (la connexion doit automatiquement se fermer)
- Après la portée, affichez
"Connection out of scope"
- Créez une portée à l’aide d’accolades
L’intérêt de RAII est que vous n’avez pas besoin de vous souvenir de fermer la connexion : le destructeur s’en charge automatiquement lorsque l’objet quitte sa portée. Ce modèle évite les fuites de ressources, même si un événement inattendu se produit dans votre code.
Incluez votre fichier d’en-tête dans main.cpp à l’aide de #include "Connection.h".
Essayez vous-même
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
string connectionId;
string message;
// Lire l'entrée
cin >> connectionId;
cin.ignore();
getline(cin, message);
// TODO: Créer une portée en utilisant des accolades { }
// À l'intérieur de la portée :
// - Créer un objet Connection sur la pile avec connectionId
// - Utiliser la connexion pour envoyer le message
// Laisser la portée se terminer (le destructeur sera appelé automatiquement)
// TODO: After the scope ends, print "Connection out of scope"
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