RAII
Resource Acquisition Is Initialization ou RAII, est une technique de programmation C++ [1] [2] qui lie le cycle de vie d'une ressource qui doit être acquise avant utilisation (mémoire heap allouée, thread d'exécution, socket ouvert, fichier ouvert, mutex verrouillé, espace disque, connexion de base de données—tout ce qui existe en quantité limitée) à la durée de vie d'un objet.
RAII garantit que la ressource est disponible pour toute fonction pouvant accéder à l'objet (la disponibilité de la ressource est un invariant de classe , éliminant les tests d'exécution redondants). Il garantit également que toutes les ressources sont libérées lorsque la durée de vie de leur objet contrôleur se termine, dans l'ordre inverse de leur acquisition. De même, si l'acquisition de la ressource échoue (le constructeur se termine par une exception), toutes les ressources acquises par chaque membre et sous-objet de base entièrement construit sont libérées dans l'ordre inverse de l'initialisation. Cela exploite les fonctionnalités fondamentales du langage ( durée de vie des objets , sortie de portée , ordre d'initialisation et déroulement de pile ) pour éliminer les fuites de ressources et garantir la sécurité face aux exceptions. Un autre nom pour cette technique est Gestion des Ressources Liées à la Portée (Scope-Bound Resource Management - SBRM), d'après le cas d'utilisation fondamental où la durée de vie d'un objet RAII se termine en raison d'une sortie de portée.
RAII peut être résumé comme suit :
- encapsuler chaque ressource dans une classe, où
-
- le constructeur acquiert la ressource et établit tous les invariants de classe ou lance une exception si cela ne peut pas être fait,
- le destructeur libère la ressource et ne lance jamais d'exceptions ;
- utilisez toujours la ressource via une instance d'une classe RAII qui soit
-
- a une durée de stockage automatique ou une durée de vie temporaire elle-même, ou
- a une durée de vie limitée par la durée de vie d'un objet automatique ou temporaire.
|
La sémantique de déplacement permet le transfert de ressources et de propriété entre objets, à l'intérieur et à l'extérieur des conteneurs, et à travers les threads, tout en garantissant la sécurité des ressources. |
(since C++11) |
Les classes avec
open()
/
close()
,
lock()
/
unlock()
, ou
init()
/
copyFrom()
/
destroy()
sont des exemples typiques de classes non-RAII :
std::mutex m; void bad() { m.lock(); // acquiert le mutex f(); // si f() lance une exception, le mutex n'est jamais libéré if (!everything_ok()) return; // retour anticipé, le mutex n'est jamais libéré m.unlock(); // si bad() atteint cette instruction, le mutex est libéré } void good() { std::lock_guard<std::mutex> lk(m); // classe RAII : l'acquisition du mutex est l'initialisation f(); // si f() lance une exception, le mutex est libéré if (!everything_ok()) return; // retour anticipé, le mutex est libéré } // si good() se termine normalement, le mutex est libéré
La bibliothèque standard
Les classes de bibliothèque C++ qui gèrent leurs propres ressources suivent le principe RAII : std::string , std::vector , std::jthread (depuis C++20) , et bien d'autres acquièrent leurs ressources dans les constructeurs (qui lancent des exceptions en cas d'erreurs), les libèrent dans leurs destructeurs (qui ne lancent jamais d'exceptions), et ne nécessitent pas de nettoyage explicite.
|
En outre, la bibliothèque standard propose plusieurs wrappers RAII pour gérer les ressources fournies par l'utilisateur :
|
(depuis C++11) |
Notes
RAII ne s'applique pas à la gestion des ressources qui ne sont pas acquises avant utilisation : temps CPU, disponibilité des cœurs, capacité du cache, capacité du pool d'entropie, bande passante réseau, consommation électrique, mémoire de pile. Pour de telles ressources, un constructeur de classe C++ ne peut garantir la disponibilité des ressources pendant la durée de vie de l'objet, et d'autres moyens de gestion des ressources doivent être utilisés.