Exceptions
La gestion des exceptions offre un moyen de transférer le contrôle et les informations d'un point de l'exécution d'un programme vers un gestionnaire associé à un point précédemment passé par l'exécution (en d'autres termes, la gestion des exceptions transfère le contrôle vers le haut de la pile d'appels).
L'évaluation d'une throw expression va lancer une exception. Les exceptions peuvent également être lancées dans d'autres contextes .
Pour qu'une exception soit capturée, l'expression throw doit être à l'intérieur d'un try block , et le try block doit contenir un handler qui correspond au type de l'objet exception.
Lors de la déclaration d'une fonction, la ou les spécifications suivantes peuvent être fournies pour limiter les types d'exceptions qu'une fonction peut lever :
| (jusqu'à C++17) |
| (depuis C++11) |
Les erreurs qui surviennent pendant la gestion des exceptions sont traitées par std::terminate et std::unexpected (jusqu'en C++17) .
Table des matières |
Utilisation
Bien que throw puisse être utilisé pour transférer le contrôle vers un bloc de code arbitraire plus haut dans la pile d'exécution, pour des raisons arbitraires (similaire à std::longjmp ), son utilisation prévue est la gestion d'erreurs.
Gestion des erreurs
Lever une exception est utilisée pour signaler des erreurs depuis des fonctions, où les "erreurs" sont généralement limitées uniquement aux cas suivants [1] [2] [3] :
- Les manquements aux postconditions, tels que l'incapacité à produire un objet de retour valide.
- Les manquements aux préconditions d'une autre fonction qui doit être appelée.
- (pour les fonctions membres non privées) Les manquements à (r)établir un invariant de classe.
En particulier, cela implique que les échecs des constructeurs (voir aussi RAII ) et la plupart des opérateurs doivent être signalés en lançant des exceptions.
De plus, les fonctions dites à contrat large utilisent les exceptions pour indiquer des entrées inacceptables, par exemple, std::basic_string::at n'a pas de préconditions, mais lance une exception pour indiquer un indice hors limites.
Sécurité des exceptions
Après qu'une condition d'erreur est signalée par une fonction, des garanties supplémentaires peuvent être fournies concernant l'état du programme. Les quatre niveaux suivants de garantie d'exception sont généralement reconnus [4] [5] [6] , qui sont des sur-ensembles stricts les uns des autres :
-
Garantie d'exception Nothrow (ou nofail)
— la fonction ne lève jamais d'exceptions. Nothrow (les erreurs sont signalées par d'autres moyens ou masquées) est attendu des
destructeurs
et autres fonctions qui peuvent être appelées pendant le déroulement de la pile.
Les
destructeurs
sont
noexceptpar défaut. (depuis C++11) Nofail (la fonction réussit toujours) est attendu des swaps, move constructors , et autres fonctions utilisées par celles qui fournissent la garantie d'exception forte. - Garantie d'exception forte — Si la fonction lève une exception, l'état du programme est restauré à l'état juste avant l'appel de la fonction (par exemple, std::vector::push_back ).
- Garantie d'exception basique — Si la fonction lève une exception, le programme est dans un état valide. Aucune ressource n'est fuitée, et tous les invariants des objets sont intacts.
- Aucune garantie d'exception — Si la fonction lève une exception, le programme peut ne pas être dans un état valide : des fuites de ressources, une corruption de mémoire, ou d'autres erreurs destructrices d'invariants peuvent s'être produites.
Les composants génériques peuvent, en outre, offrir une
garantie d'exception-neutre
: si une exception est levée par un paramètre de modèle (par exemple par la
Compare
fonction objet de
std::sort
ou par le constructeur de
T
dans
std::make_shared
), elle est propagée, inchangée, à l'appelant.
Objets d'exception
Bien que des objets de tout type complet et des pointeurs cv vers void puissent être lancés comme objets d'exception, toutes les fonctions de la bibliothèque standard lancent des objets anonymes par valeur, et les types de ces objets sont dérivés (directement ou indirectement) de std::exception . Les exceptions définies par l'utilisateur suivent généralement ce modèle. [7] [8] [9]
Pour éviter la copie inutile de l'objet d'exception et le slicing d'objet, la meilleure pratique pour les gestionnaires est de capturer par référence. [10] [11] [12] [13]
Notes
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_constexpr_exceptions
|
202411L
|
(C++26) | constexpr exceptions |
Liens externes
|