En concevant ma première bibliothèque C ++ «sérieuse», je me pose la question:
Est-ce un bon style de dériver ses exceptions std::exception
et ses descendants?!
Même après avoir lu
- Conception de classes d'exception
- Qu'est-ce qu'un «bon nombre» d'exceptions à implémenter pour ma bibliothèque?
Je ne suis toujours pas sûr. Parce que, en plus des pratiques courantes (mais peut-être pas bonnes), je suppose, en tant qu'utilisateur de bibliothèque, qu'une fonction de bibliothèque ne lancerait std::exception
s que lorsque les fonctions de bibliothèque standard échouent dans l'implémentation de la bibliothèque, et elle ne peut rien y faire. Mais quand même, lors de l'écriture du code d'application, pour moi, c'est très pratique, et aussi à mon humble avis, il suffit de jeter un std::runtime_error
. Mes utilisateurs peuvent également compter sur l'interface minimale définie, comme what()
ou sur des codes.
Et par exemple, mon utilisateur fournit des arguments défectueux, quoi de plus pratique que de lancer un std::invalid_argument
, n'est-ce pas? Donc, combiné avec l'utilisation encore courante de std :: exception, je vois dans le code d'autres: Pourquoi ne pas aller plus loin et dériver de votre classe d'exception personnalisée (par exemple lib_foo_exception) et aussi de std::exception
.
Pensées?
la source
std::exception
n'est pas parce que vous héritez de que vous lancez unstd::exception
. En outre,std::runtime_error
héritestd::exception
en premier lieu, et lawhat()
méthode vientstd::exception
, nonstd::runtime_error
. Et vous devez absolument créer vos propres classes d'exceptions au lieu de lancer des exceptions génériques telles questd::runtime_error
.lib_foo_exception
classe dérivestd::exception
, l'utilisateur de la bibliothèque attraperaitlib_foo_exception
simplement en attrapantstd::exception
, en plus de ne capturer que la bibliothèque. Je pourrais donc aussi demander à ma classe racine d'exception de bibliothèque d'hériter de std :: exception .lib_foo_exception
?" Avec l'héritage destd::exception
vous pouvez le faire parcatch(std::exception)
OU parcatch(lib_foo_exception)
. Sans dériver destd::exception
, vous l'attraperiez si et seulement si , parcatch(lib_foo_exception)
.catch(...)
. C'est là parce que le langage permet le cas que vous envisagez (et pour les bibliothèques "mal se comporter"), mais ce n'est pas la meilleure pratique moderne.catch
sites plus grossiers et plus généraux , ainsi que les transactions plus grossières qui modélisent une opération utilisateur. Si vous le comparez à des langages qui ne promeuvent pas l'idée d'une capture généraliséestd::exception&
, par exemple, ils ont souvent beaucoup plus de code avec destry/catch
blocs intermédiaires concernés par des erreurs très spécifiques, ce qui diminue quelque peu la généralité du traitement des exceptions car il commence à se placer un accent beaucoup plus marqué sur la gestion manuelle des erreurs, ainsi que sur toutes les erreurs disparates qui pourraient éventuellement se produire.Réponses:
Toutes les exceptions doivent hériter de
std::exception
.Supposons, par exemple, que j'aie besoin d'appeler
ComplexOperationThatCouldFailABunchOfWays()
et que je souhaite gérer toutes les exceptions qu'il pourrait déclencher. Si tout héritestd::exception
, c'est facile. Je n'ai besoin que d'un seulcatch
bloc, et j'ai une interface standard (what()
) pour obtenir des détails.Si les exceptions n'héritent PAS de
std::exception
, cela devient beaucoup plus laid:En ce qui concerne l'opportunité de lancer
runtime_error
ou deinvalid_argument
créer vos propresstd::exception
sous-classes à lancer: Ma règle de base est d'introduire une nouvelle sous-classe chaque fois que j'ai besoin de traiter un type d'erreur particulier différemment des autres erreurs (c'est-à-dire chaque fois que j'ai besoin d'uncatch
bloc séparé ).runtime_error
jeté ici signifie quelque chose de différent d'une erreur d'exécution générique), alors je risque d'entrer en conflit avec d' autres utilisations de la sous - classe existante.invalid_argument
), alors je réutilise la classe existante. Je ne vois tout simplement pas beaucoup d'avantages à ajouter une nouvelle classe dans ce cas. (Les directives de base C ++ ne sont pas d'accord avec moi ici - elles recommandent toujours d'utiliser vos propres classes.)Les directives de base C ++ contiennent d'autres discussions et exemples.
la source
catch (...)
(avec les points de suspension littéraux)?catch (...)
est utile uniquement si vous n'avez pas besoin de faire quoi que ce soit avec ce qui est jeté. Si vous voulez faire quelque chose - par exemple, afficher ou enregistrer le message d'erreur spécifique, comme dans mon exemple - alors vous devez savoir ce que c'est.C'est une hypothèse incorrecte.
Les types d'exceptions standard sont fournis pour une utilisation "plus courante". Ils ne sont pas conçus pour être utilisés uniquement par la bibliothèque standard.
Oui, faites tout hériter de tout
std::exception
. Souvent, cela impliquera l'héritage destd::runtime_error
oustd::logic_error
. Tout ce qui est approprié pour la classe d'exception que vous implémentez.Ceci est bien sûr subjectif - plusieurs bibliothèques populaires ignorent complètement les types d'exceptions standard, vraisemblablement pour dissocier les bibliothèques de la bibliothèque standard. Personnellement, je pense que c'est extrêmement égoïste! Cela rend la capture d'exceptions beaucoup plus difficile à corriger.
En parlant personnellement, je lance souvent un
std::runtime_error
et j'en ai fini avec. Mais c'est entrer dans une discussion sur la granularité de vos classes d'exception, ce qui n'est pas ce que vous demandez.la source