Bien que je sois d’accord pour dire que capturer ...
sans renverser est en effet une erreur, je pense cependant que l’utilisation de constructions comme celle-ci:
try
{
// Stuff
}
catch (...)
{
// Some cleanup
throw;
}
Est acceptable dans les cas où RAII n'est pas applicable . (S'il vous plaît, ne demandez pas ... tout le monde dans mon entreprise n'aime pas la programmation orientée objet et RAII est souvent considéré comme un "matériel scolaire inutile" ...)
Mes collègues disent que vous devez toujours savoir quelles exceptions doivent être levées et que vous pouvez toujours utiliser des constructions telles que:
try
{
// Stuff
}
catch (exception_type1&)
{
// Some cleanup
throw;
}
catch (exception_type2&)
{
// Some cleanup
throw;
}
catch (exception_type3&)
{
// Some cleanup
throw;
}
Existe-t-il une bonne pratique bien admise concernant ces situations?
...
" alors que ma question se concentre sur "Dois-je mieux attraper...
ou<specific exception>
avant de retransmettre"main
,catch(...) { return EXIT_FAILURE; }
pourrait bien avoir raison dans un code qui ne s'exécute pas sous un débogueur. Si vous n'attrapez pas, la pile peut ne pas être déroulée. Ce n'est que lorsque votre débogueur détecte des exceptions non interceptées que vous souhaitez les laissermain
.Réponses:
Je détesterais le dire à votre collègue, de toute évidence, n’a jamais travaillé sur des bibliothèques polyvalentes.
Comment dans le monde une classe semblable peut-elle
std::vector
même prétendre savoir ce que les constructeurs de copies vont lancer, tout en garantissant la sécurité des exceptions?Si vous saviez toujours ce que l'appelant ferait au moment de la compilation, le polymorphisme serait inutile! Parfois, l’ objectif entier est de faire abstraction de ce qui se passe à un niveau inférieur, de sorte que vous ne voulez surtout pas savoir ce qui se passe!
la source
...
?Vous semblez être pris au piège de quelqu'un qui essaie de manger son gâteau et de le manger.
La RAII et les exceptions sont conçues pour aller de pair. RAII est le moyen par lequel vous n'avez à écrire beaucoup de
catch(...)
déclarations à faire le nettoyage. Cela se produira automatiquement, bien sûr. Et les exceptions sont le seul moyen de travailler avec des objets RAII, car les constructeurs ne peuvent que réussir ou lancer (ou placer l'objet dans un état d'erreur, mais qui le souhaite?).Une
catch
déclaration peut faire l’une des deux choses suivantes: gérer une erreur ou des circonstances exceptionnelles, ou effectuer des travaux de nettoyage. Parfois, il fait les deux, mais chaquecatch
déclaration existe pour faire au moins une de celles-ci.catch(...)
est incapable de gérer correctement les exceptions. Vous ne savez pas quelle est l'exception; vous ne pouvez pas obtenir d'informations sur l'exception. Vous n'avez absolument aucune information autre que le fait qu'une exception a été levée par quelque chose dans un certain bloc de code. La seule chose légitime que vous puissiez faire dans un tel blocage est de faire le ménage. Et cela signifie relancer l'exception à la fin du nettoyage.Ce que RAII vous donne en ce qui concerne la gestion des exceptions est un nettoyage gratuit. Si tout est correctement encapsulé dans RAII, tout sera nettoyé correctement. Vous n'avez plus besoin que les
catch
déclarations nettoient. Dans ce cas, il n'y a aucune raison d'écrire unecatch(...)
déclaration.Donc, je conviens que
catch(...)
c'est surtout du mal ... provisoirement .Cette disposition étant le bon usage de RAII. Parce que sans cela, vous devez pouvoir effectuer certains travaux de nettoyage. Il n'y a pas moyen de s'en sortir; vous devez être capable de faire le travail de nettoyage. Vous devez pouvoir vous assurer que le lancement d'une exception laissera le code dans un état raisonnable. Et
catch(...)
est un outil essentiel pour le faire.Tu ne peux pas en avoir un sans avoir l'autre. Vous ne pouvez pas dire que les deux RAII et
catch(...)
sont mauvais. Vous avez besoin d'au moins un d'entre eux. sinon, vous n'êtes pas exceptionnellement en sécurité.Bien sûr, il y a une utilisation valable, bien que rare,
catch(...)
que même la RAII ne puisse bannir: laexception_ptr
transmission à quelqu'un d'autre. Généralement via unepromise/future
interface ou similaire.Votre collègue est un idiot (ou tout simplement terriblement ignorant). Cela devrait être immédiatement évident en raison de la quantité de code copier-coller qu'il suggère que vous écriviez. Le nettoyage de chacune de ces déclarations de capture sera exactement le même . C'est un cauchemar d'entretien, sans parler de la lisibilité.
En bref: c’est le problème que RAII a été créé pour résoudre (non pas qu’il ne résout pas d’autres problèmes).
Ce qui me déroute dans cette idée, c'est que c'est généralement à l'envers que la plupart des gens prétendent que RAII est mauvais. En général, l'argument est le suivant: "RAII est mauvais, car vous devez utiliser des exceptions pour signaler une défaillance du constructeur. Mais vous ne pouvez pas lancer d'exceptions, car ce n'est pas sûr et vous devez disposer de nombreuses
catch
instructions pour tout nettoyer." Ce qui est un argument cassé parce que RAII résout le problème créé par le manque de RAII.Plus que probablement, il est contre RAII parce qu'il cache des détails. Les appels de destructeurs ne sont pas immédiatement visibles sur les variables automatiques. Donc, vous obtenez un code qui est appelé implicitement. Certains programmeurs détestent vraiment ça. Apparemment, au point où ils pensent avoir 3
catch
déclarations qui font toutes la même chose avec du code copier-coller est une meilleure idée.la source
Deux commentaires, vraiment. La première est que, même dans un monde idéal, vous devriez toujours savoir quelles exceptions peuvent être levées. En pratique, si vous utilisez des bibliothèques tierces ou si vous compilez avec un compilateur Microsoft, vous ne le savez pas. Plus précisément, cependant; même si vous connaissez exactement toutes les exceptions possibles, est-ce pertinent ici?
catch (...)
exprime l’intention bien mieux quecatch ( std::exception const& )
, même en supposant que toutes les exceptions possibles découlent destd::exception
(ce qui serait le cas dans un monde idéal). En ce qui concerne l’utilisation de plusieurs blocs de capture, s’il n’existe pas de base commune pour toutes les exceptions: il s’agit là d’un obscurcissement total et d’un cauchemar d’entretien. Comment reconnaissez-vous que tous les comportements sont identiques? Et que c'était l'intention? Et que se passe-t-il si vous devez modifier le comportement (résolution de bogue, par exemple)? C'est trop facile d'en manquer un.la source
std::exception
et essaie tous les jours d’imposer son utilisation à notre base de code. J'imagine qu'il essaie de me punir pour avoir utilisé du code et des bibliothèques externes qu'il n'a pas écrites.std::vector<>
peut lancer n'importe quelle exception pour à peu près n'importe quelle raison.Je pense que votre collègue a mélangé quelques bons conseils - vous ne devriez gérer les exceptions connues que dans un
catch
bloc lorsque vous ne les relancez pas.Ça signifie:
Est mauvais car il cachera silencieusement toute erreur.
Pourtant:
C'est bon - nous savons ce que nous avons à faire et n'avons pas besoin de l'exposer au code d'appel.
Également:
C’est bien, même selon les meilleures pratiques, le code permettant de traiter les erreurs générales devrait être lié au code qui les provoque. C'est mieux que de compter sur l'appelé pour savoir qu'une transaction doit être annulée ou peu importe.
la source
Toute réponse par oui ou par non doit être accompagnée d'une justification expliquant pourquoi il en est ainsi.
Dire que c'est faux simplement parce que j'ai appris de cette façon, c'est du fanatisme aveugle.
Écrire la même chose
//Some cleanup; throw
plusieurs fois, comme dans votre exemple, est une erreur car il s'agit d'une duplication de code, ce qui représente une charge de maintenance. L'écrire juste une fois, c'est mieux.Écrire une commande
catch(...)
pour réduire au silence toutes les exceptions est une erreur, car vous ne devez gérer que les exceptions que vous savez manipuler. Grâce à ce caractère générique, vous pouvez effectuer plus de tâches que vous ne le souhaitez et faire taire les erreurs importantes.Mais si vous redemandez après a
catch(...)
, la dernière raison ne s'applique plus, car vous ne gérez pas l'exception, il n'y a donc aucune raison pour que cela soit découragé.En fait, je l'ai fait pour la connexion de fonctions sensibles sans aucun problème:
la source
Log(...)
ne peut pas jeter.Je suis généralement d’accord avec l’ambiance des articles. Je n'aime pas vraiment le modèle d’attrapage d’exceptions spécifiques. Je pense que la syntaxe de cette méthode en est encore à ses balbutiements et qu’elle n’est pas encore capable de gérer le code redondant.
Mais puisque tout le monde le dit, je dirai que même si je les utilise avec parcimonie, j’ai souvent regardé l’une de mes déclarations "catch (exception e)" et déclaré: "Bon sang, j’aurais aimé appeler les exceptions spécifiques de cette époque "parce que quand on rentre plus tard, il est souvent agréable de savoir quelle était l'intention et ce que le client est susceptible de jeter en un coup d'oeil.
Je ne justifie pas l'attitude de «Toujours utiliser x», mais simplement dire qu'il est parfois agréable de les voir répertoriées et je suis sûr que certaines personnes pensent que c'est la «bonne» façon de faire.
la source