Il existe d'excellentes réponses, alors j'ajoute simplement des choses oubliées.
0. RAII concerne les étendues
RAII concerne les deux:
- acquérir une ressource (quelle que soit la ressource) dans le constructeur et la désacquérir dans le destructeur.
- avoir le constructeur exécuté lorsque la variable est déclarée, et le destructeur automatiquement exécuté lorsque la variable sort de la portée.
D'autres ont déjà répondu à ce sujet, je ne vais donc pas m'étendre davantage.
1. Lors du codage en Java ou C #, vous utilisez déjà RAII ...
MONSIEUR JOURDAIN: Quoi! Quand je dis: «Nicole, apporte-moi mes pantoufles et donne-moi mon dernier verre», c'est de la prose?
MAÎTRE DE PHILOSOPHIE: Oui, Monsieur.
MONSIEUR JOURDAIN: Depuis plus de quarante ans, je parle de la prose sans rien en savoir, et je vous suis bien obligé de m'avoir appris cela.
- Molière: Le gentilhomme de la classe moyenne, acte 2, scène 4
Comme Monsieur Jourdain l'a fait avec la prose, les gens de C # et même de Java utilisent déjà RAII, mais de manière cachée. Par exemple, le code Java suivant (qui s'écrit de la même manière en C # en remplaçant synchronized
par lock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
... utilise déjà RAII: L'acquisition du mutex se fait dans le mot-clé ( synchronized
ou lock
), et la désacquisition se fera à la sortie du scope.
C'est tellement naturel dans sa notation qu'il ne nécessite presque aucune explication, même pour les personnes qui n'ont jamais entendu parler de RAII.
L'avantage de C ++ sur Java et C # ici est que tout peut être fait en utilisant RAII. Par exemple, il n'y a pas d'équivalent intégré direct de synchronized
ni lock
en C ++, mais nous pouvons toujours les avoir.
En C ++, il s'écrirait:
void foo()
{
// etc.
{
Lock lock(someObject) ; // lock is an object of type Lock whose
// constructor acquires a mutex on
// someObject and whose destructor will
// un-acquire it
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
qui peut être facilement écrit à la manière Java / C # (en utilisant des macros C ++):
void foo()
{
// etc.
LOCK(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
2. RAII a d'autres utilisations
WHITE RABBIT: [chantant] Je suis en retard / Je suis en retard / Pour une date très importante. / Pas le temps de dire "Bonjour". / Au revoir. / Je suis en retard, je suis en retard, je suis en retard.
- Alice au pays des merveilles (version Disney, 1951)
Vous savez quand le constructeur sera appelé (à la déclaration de l'objet), et vous savez quand son destructeur correspondant sera appelé (à la sortie de la portée), vous pouvez donc écrire du code presque magique avec une ligne seulement. Bienvenue au pays des merveilles du C ++ (du moins du point de vue d'un développeur C ++).
Par exemple, vous pouvez écrire un objet compteur (je laisse cela comme un exercice) et l'utiliser simplement en déclarant sa variable, comme l'objet de verrouillage ci-dessus a été utilisé:
void foo()
{
double timeElapsed = 0 ;
{
Counter counter(timeElapsed) ;
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
qui bien sûr, peut être écrit, encore une fois, de manière Java / C # en utilisant une macro:
void foo()
{
double timeElapsed = 0 ;
COUNTER(timeElapsed)
{
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
3. Pourquoi le C ++ manque- finally
t-il?
[CRIER] C'est le compte à rebours final !
- Europe: le compte à rebours final (désolé, j'étais à court de citations, ici ... :-)
La finally
clause est utilisée en C # / Java pour gérer l'élimination des ressources en cas de sortie d'étendue (via une return
exception ou une exception levée).
Les lecteurs de spécifications astucieux auront remarqué que C ++ n'a pas de clause finally. Et ce n'est pas une erreur, car C ++ n'en a pas besoin, car RAII gère déjà l'élimination des ressources. (Et croyez-moi, écrire un destructeur C ++ est plus facile que d'écrire la bonne clause Java finally, ou même la méthode Dispose correcte d'un C #).
Pourtant, parfois, une finally
clause serait cool. Pouvons-nous le faire en C ++? Oui nous pouvons! Et encore une fois avec une utilisation alternative de RAII.
Conclusion: RAII est plus qu'une philosophie en C ++: c'est du C ++
RAII? CECI EST C ++ !!!
- Commentaire indigné du développeur C ++, copié sans vergogne par un obscur roi Sparte et ses 300 amis
Lorsque vous atteignez un certain niveau d'expérience en C ++, vous commencez à penser en termes de RAII , en termes d' exécution automatisée des constructeurs et des destructeurs .
Vous commencez à penser en termes de portées , et les caractères {
et }
deviennent l'un des plus importants de votre code.
Et presque tout convient en termes de RAII: sécurité des exceptions, mutex, connexions de base de données, requêtes de base de données, connexion au serveur, horloges, poignées de système d'exploitation, etc., et enfin, la mémoire.
La partie base de données n'est pas négligeable, car, si vous acceptez de payer le prix, vous pouvez même écrire dans un style « programmation transactionnelle », en exécutant des lignes et des lignes de code jusqu'à décider, au final, si vous voulez valider tous les changements , ou, si ce n'est pas possible, avoir toutes les modifications annulées (tant que chaque ligne satisfait au moins la garantie d'exception forte). (voir la deuxième partie de cet article de Herb's Sutter pour la programmation transactionnelle).
Et comme un puzzle, tout va bien.
RAII fait tellement partie de C ++ que C ++ ne pourrait pas être C ++ sans lui.
Cela explique pourquoi les développeurs C ++ expérimentés sont si amoureux de RAII, et pourquoi RAII est la première chose qu'ils recherchent lorsqu'ils essaient un autre langage.
Et cela explique pourquoi le Garbage Collector, bien qu'il s'agisse d'une technologie magnifique en soi, n'est pas si impressionnant du point de vue d'un développeur C ++:
- RAII gère déjà la plupart des dossiers traités par un GC
- Un GC traite mieux que RAII avec des références circulaires sur des objets gérés purs (atténués par des utilisations intelligentes de pointeurs faibles)
- Encore un GC est limité à la mémoire, tandis que RAII peut gérer tout type de ressource.
- Comme décrit ci-dessus, RAII peut faire beaucoup, beaucoup plus ...