J'ai vu au moins une source fiable (une classe C ++ que j'ai prise) recommander que les classes d'exceptions spécifiques à l'application en C ++ devraient hériter de std::exception
. Je ne suis pas sûr des avantages de cette approche.
En C #, les raisons d'hériter de ApplicationException
sont claires: vous obtenez une poignée de méthodes, de propriétés et de constructeurs utiles et vous n'avez qu'à ajouter ou remplacer ce dont vous avez besoin. Avec std::exception
il semble que tout ce que vous obtenez est une what()
méthode de remplacement, que vous pouvez tout aussi bien créer vous-même.
Alors, quels sont les avantages, le cas échéant, de l'utilisation std::exception
comme classe de base pour ma classe d'exceptions spécifique à l'application? Y a-t-il de bonnes raisons de ne pas hériter std::exception
?
la source
Réponses:
Le principal avantage est que le code utilisant vos classes n'a pas besoin de connaître le type exact de ce que vous faites
throw
, mais peut simplementcatch
lestd::exception
.Edit: comme Martin et d'autres l'ont noté, vous voulez en fait dériver de l'une des sous-classes
std::exception
déclarées dans l'en-<stdexcept>
tête.la source
std::exception
est défini dans l'en-<exception>
tête ( preuve ).-1
Le problème avec
std::exception
est qu'il n'y a pas de constructeur (dans les versions conformes à la norme) qui accepte un message.En conséquence, je préfère dériver de
std::runtime_error
. Ceci est dérivé destd::exception
mais ses constructeurs vous permettent de passer une C-String ou unstd::string
au constructeur qui sera retourné (en tant que achar const*
) lors de l'what()
appel.la source
La raison de l'héritage de
std::exception
cette classe de base "standard" pour les exceptions, il est donc naturel pour les autres membres d'une équipe, par exemple, de s'attendre à cela et d'attraper la basestd::exception
.Si vous recherchez la commodité, vous pouvez hériter de
std::runtime_error
cestd::string
constructeur fournit .la source
std::exception
est d' ailleurs une excellente idée. Ce que vous ne devriez pas faire, c'est hériter de plus d'un.Une fois, j'ai participé au nettoyage d'une grande base de code où les auteurs précédents avaient jeté des ints, HRESULTS, std :: string, char *, des classes aléatoires ... des trucs différents partout; nommez simplement un type et il a probablement été jeté quelque part. Et pas du tout de classe de base commune. Croyez-moi, les choses étaient beaucoup plus ordonnées une fois que nous sommes arrivés au point que tous les types lancés avaient une base commune que nous pouvions attraper et savoir que rien n'allait passer. Alors faites-vous plaisir (et à ceux qui devront maintenir votre code à l'avenir) une faveur et faites-le de cette façon dès le début.
la source
Vous devez hériter de boost :: exception . Il fournit beaucoup plus de fonctionnalités et des moyens bien compris de transporter des données supplémentaires ... bien sûr, si vous n'utilisez pas Boost , ignorez cette suggestion.
la source
Oui, vous devriez dériver
std::exception
.D'autres ont répondu que
std::exception
le problème était que vous ne pouvez pas lui transmettre un message texte, mais ce n'est généralement pas une bonne idée d'essayer de formater un message utilisateur au moment du lancer. À la place, utilisez l'objet d'exception pour transporter toutes les informations pertinentes vers le site de capture qui peut ensuite formater un message convivial.la source
std::exception
et porter les informations de l'exception. Mais qui sera responsable de la création / du formatage du message d'erreur? La::what()
fonction ou autre chose?La raison pour laquelle vous voudrez peut-être hériter de
std::exception
est parce que cela vous permet de lever une exception qui est interceptée selon cette classe, c'est-à-dire:la source
Il y a un problème avec l'héritage que vous devriez connaître est le découpage d'objets. Lorsque vous écrivez
throw e;
une expression throw initialise un objet temporaire, appelé objet d'exception, dont le type est déterminé en supprimant tous les qualificatifs cv de niveau supérieur du type statique de l'opérande dethrow
. Ce n'est peut-être pas ce à quoi vous vous attendez. Exemple de problème que vous pourriez trouver ici .Ce n'est pas un argument contre l'héritage, c'est juste une information «à connaître».
la source
throw;
ça va, mais il n'est pas évident que vous deviez écrire quelque chose comme ça.raise()
en tant que tel:virtual void raise() { throw *this; }
. N'oubliez pas de le remplacer dans chaque exception dérivée.Différence: std :: runtime_error vs std :: exception ()
Que vous deviez en hériter ou non, cela dépend de vous. Standard
std::exception
et ses descendants standard proposent une structure hiérarchique d'exception possible (division en sous-logic_error
hiérarchie et sous-runtime_error
hiérarchie) et une interface d'objet d'exception possible. Si vous l'aimez, utilisez-le. Si pour une raison quelconque vous avez besoin de quelque chose de différent, définissez votre propre cadre d'exceptions.la source
Puisque le langage lance déjà std :: exception, vous devez quand même l'attraper pour fournir un rapport d'erreur correct. Vous pouvez également utiliser la même capture pour toutes les exceptions inattendues de votre choix. De plus, presque toutes les bibliothèques qui lèvent des exceptions les dériveraient de std :: exception.
En d'autres termes, c'est soit
ou
Et la deuxième option est certainement meilleure.
la source
Si toutes vos exceptions possibles dérivent
std::exception
, votre bloc catch peut simplementcatch(std::exception & e)
et être assuré de tout capturer.Une fois que vous avez capturé l'exception, vous pouvez utiliser cette
what
méthode pour obtenir plus d'informations. C ++ ne prend pas en charge le typage canard, donc une autre classe avec unewhat
méthode nécessiterait une capture et un code différents pour l'utiliser.la source
La première question est de savoir s'il faut dériver d'un type d'exception standard ou non. Cela active un seul gestionnaire d'exceptions pour toutes les exceptions de bibliothèque standard et les vôtres, mais cela encourage également de tels gestionnaires fourre-tout. Le problème est qu'il ne faut attraper que les exceptions que l'on sait gérer. Dans main (), par exemple, intercepter toutes les exceptions std :: est probablement une bonne chose si la chaîne what () sera enregistrée en dernier recours avant de quitter. Ailleurs, cependant, il est peu probable que ce soit une bonne idée.
Une fois que vous avez décidé de dériver ou non d'un type d'exception standard, la question est de savoir qui devrait être la base. Si votre application n'a pas besoin d'i18n, vous pourriez penser que le formatage d'un message sur le site d'appel est tout aussi bon que d'enregistrer des informations et de générer le message sur le site d'appel. Le problème est que le message formaté n'est peut-être pas nécessaire. Mieux vaut utiliser un schéma de génération de message paresseux - peut-être avec une mémoire préallouée. Ensuite, si le message est nécessaire, il sera généré lors de l'accès (et éventuellement mis en cache dans l'objet d'exception). Ainsi, si le message est généré lorsqu'il est lancé, alors un dérivé std :: exception, comme std :: runtime_error, est nécessaire comme classe de base. Si le message est généré paresseusement, alors std :: exception est la base appropriée.
la source
Une autre raison de sous-classes d'exceptions est un meilleur aspect de conception lorsque vous travaillez sur de grands systèmes encapsulés. Vous pouvez le réutiliser pour des choses telles que les messages de validation, les requêtes des utilisateurs, les erreurs fatales du contrôleur, etc. Plutôt que de réécrire ou de relancer toutes vos validations comme des messages, vous pouvez simplement les «attraper» sur le fichier source principal, mais lancer l'erreur n'importe où dans votre ensemble complet de classes.
Par exemple, une exception fatale mettra fin au programme, une erreur de validation effacera uniquement la pile et une requête de l'utilisateur posera une question à l'utilisateur final.
Cela signifie également que vous pouvez réutiliser les mêmes classes mais sur des interfaces différentes. Par exemple, une application Windows peut utiliser une boîte de message, un service Web affichera le code HTML et le système de rapport l'enregistrera et ainsi de suite.
la source
Bien que cette question soit plutôt ancienne et ait déjà reçu de nombreuses réponses, je veux juste ajouter une note sur la façon de gérer correctement les exceptions en C ++ 11, car cela me manque continuellement dans les discussions sur les exceptions:
Utiliser
std::nested_exception
etstd::throw_with_nested
Il est décrit sur StackOverflow ici et ici , comment vous pouvez obtenir une trace de vos exceptions dans votre code sans avoir besoin d'un débogueur ou d'une journalisation encombrante, en écrivant simplement un gestionnaire d'exceptions approprié qui renverra les exceptions imbriquées.
Comme vous pouvez le faire avec n'importe quelle classe d'exception dérivée, vous pouvez ajouter beaucoup d'informations à une telle trace! Vous pouvez également jeter un œil à mon MWE sur GitHub , où une trace de retour ressemblerait à ceci:
Vous n'avez même pas besoin de sous-classe
std::runtime_error
pour obtenir beaucoup d'informations lorsqu'une exception est levée.Le seul avantage que je vois dans le sous-classement (au lieu de simplement utiliser
std::runtime_error
) est que votre gestionnaire d'exceptions peut intercepter votre exception personnalisée et faire quelque chose de spécial. Par exemple:la source