La gestion des exceptions en C ++ est limitée à essayer / lancer / attraper. Contrairement à Object Pascal, Java, C # et Python, même en C ++ 11, la finally
construction n'a pas été implémentée.
J'ai vu énormément de littérature C ++ parler de "code sécurisé d'exception". Lippman écrit que le code sécurisé d'exception est un sujet important mais complexe et complexe, qui dépasse le cadre de son introduction, ce qui semble impliquer que le code sécurisé n'est pas fondamental pour C ++. Herb Sutter consacre 10 chapitres à ce sujet dans son exceptionnel C ++!
Pourtant, il me semble que bon nombre des problèmes rencontrés lors de la tentative d'écriture de "code sécurisé d'exception" pourraient être assez bien résolus si la finally
construction était implémentée, permettant ainsi au programmeur de s'assurer que même en cas d'exception, le programme puisse être restauré. à un état sûr, stable, sans fuite, proche du point d’allocation des ressources et du code potentiellement problématique. En tant que programmeur Delphi et C # très expérimenté, j'utilise try .. finally, mais la plupart des programmeurs de ces langages bloquent assez largement dans mon code.
Considérant tous les "cloches et sifflets" mis en œuvre dans C ++ 11, j'ai été étonné de constater que "finalement" n'était toujours pas là.
Alors, pourquoi la finally
construction n'a-t-elle jamais été implémentée en C ++? Ce n'est vraiment pas un concept très difficile ou avancé à comprendre et cela aide énormément le programmeur à écrire du «code protégé par exception».
la source
finally
de langage C ++ et quelles techniques de gestion des exceptions sont utilisées à sa place?" est valide et sujet pour ce site. Les réponses existantes couvrent bien ceci, je pense. En transformant cela en une discussion sur "Les raisons pour lesquelles les concepteurs C ++ ont choisi de ne pas inclure lafinally
peine de gagner?" et "Devrait-finally
on ajouter au C ++?" et poursuivre la discussion à travers les commentaires sur la question et chaque réponse ne correspond pas au modèle de ce site de questions-réponses.Réponses:
Quelques commentaires supplémentaires sur la réponse de @ Nemanja (qui, puisqu'il cite Stroustrup, est vraiment la meilleure des réponses que vous puissiez obtenir):
Il s’agit vraiment de comprendre la philosophie et les idiomes du C ++. Prenons l'exemple d'une opération qui ouvre une connexion de base de données sur une classe persistante et doit s'assurer qu'elle ferme cette connexion si une exception est levée. Ceci est une question de sécurité des exceptions et s'applique à tout langage avec des exceptions (C ++, C #, Delphi ...).
Dans une langue qui utilise
try
/finally
, le code pourrait ressembler à ceci:Simple et simple. Il y a cependant quelques inconvénients:
finally
bloc, sinon je perds des ressources.DoRiskyOperation
est plus qu'un appel de méthode - si j'ai un traitement à effectuer dans letry
bloc - alors l'Close
opération peut finir par être un bit décent loin de l'Open
opération. Je ne peux pas écrire mon nettoyage juste à côté de mon acquisition.try
/finally
blocs en profondeur .L'approche C ++ ressemblerait à ceci:
Cela résout complètement tous les inconvénients de l'
finally
approche. Il présente quelques inconvénients, mais ils sont relativement mineurs:ScopedDatabaseConnection
vous-même le cours. Cependant, c'est une implémentation très simple - seulement 4 ou 5 lignes de code.Personnellement, compte tenu de ces avantages et inconvénients, je trouve que la RAII est une technique bien préférable
finally
. Votre kilométrage peut varier.Enfin, RAII étant un langage si bien établi en C ++, et pour soulager les développeurs de l’écriture de nombreuses
Scoped...
classes, il existe des bibliothèques telles que ScopeGuard et Boost.ScopeExit qui facilitent ce type de nettoyage déterministe.la source
using
instruction, qui nettoie automatiquement tout objet implémentant l'IDisposable
interface. Ainsi, s’il est possible de se tromper, il est assez facile de le faire correctement.try/finally
construction, car le compilateur n'expose pas detry/finally
construction et le seul moyen d'y accéder consiste à utiliser la classe idiome de conception, n’est pas un "avantage"; c'est la définition même d'une inversion d'abstraction.finally
. Comme je l'ai dit, votre kilométrage peut varier.De Pourquoi pas C ++ fournir une construction « enfin »? dans la FAQ sur le style et la technique C ++ de Bjarne Stroustrup :
la source
finally
construction est toujours inutile pour toujours, malgré ce que dit Strousup. Le simple fait que l'écriture de "code sécurisé d'exception" soit une grosse affaire en C ++ en est la preuve. Heck, C # a les deux destructeurs etfinally
, et ils s'habituent tous les deux .La raison pour laquelle C ++ n'a pas,
finally
c'est parce que cela n'est pas nécessaire en C ++.finally
est utilisé pour exécuter du code indépendamment du fait qu'une exception se soit produite ou non, ce qui est presque toujours une sorte de code de nettoyage. En C ++, ce code de nettoyage doit figurer dans le destructeur de la classe appropriée et ce destructeur sera toujours appelé, exactement comme unfinally
bloc. Le langage utilisé pour utiliser le destructeur lors de votre nettoyage s’appelle RAII .Au sein de la communauté C ++, on parle peut-être davantage de code «sauf exception», mais il est presque tout aussi important dans les autres langages dotés d'exceptions. L'intérêt du code "exception sûre" est que vous réfléchissiez à l'état dans lequel se trouve votre code si une exception se produit dans l'une des fonctions / méthodes que vous appelez.
En C ++, le code 'exception safe' est légèrement plus important, car C ++ ne dispose pas d'un garbage collection qui prend en charge les objets laissés orphelins à la suite d'une exception.
La raison pour laquelle la sécurité des exceptions est davantage discutée dans la communauté C ++ provient probablement aussi du fait qu'en C ++, vous devez être plus conscient de ce qui peut mal tourner, car il existe moins de filets de sécurité par défaut dans le langage.
la source
finally
à la norme C ++, je pense qu'il est prudent de conclure que la communauté C ++ ne considère pasthe absence of finally
un problème. La plupart des langages qui en sontfinally
dépourvus n'ont pas la destruction déterministe cohérente dont dispose le C ++. Je vois que Delphi les a tous les deux, mais je ne connais pas suffisamment son histoire pour savoir laquelle était la première.finally
". Je ne me souviendrais jamais d'une tâche que cela aurait facilitée si j'avais pu y accéder.D'autres ont évoqué la solution RAII. C'est une très bonne solution. Mais cela ne dit pas vraiment pourquoi ils n’ont pas ajouté
finally
autant car c’est une chose largement souhaitée. La réponse à cette question est plus fondamentale pour la conception et le développement de C ++: tout au long du développement de C ++, les personnes impliquées ont fermement résisté à l'introduction de fonctionnalités de conception pouvant être obtenues à l'aide d'autres fonctionnalités sans grande difficulté, en particulier lorsque cela nécessite l'introduction. de nouveaux mots-clés qui pourraient rendre l'ancien code incompatible. Comme RAII fournit une alternative très fonctionnelle àfinally
et que vous pouvez réellement rouler vous-mêmefinally
en C ++ 11, il y avait peu d’appel à faire.Tout ce que vous avez à faire est de créer une classe
Finally
qui appelle la fonction transmise à son constructeur dans son destructeur. Ensuite, vous pouvez faire ceci:En général, la plupart des programmeurs C ++ natifs préfèrent cependant les objets RAII conçus avec soin.
la source
Finally atEnd([&] () { database.close(); });
aussi, j'imagine que ce qui suit est préférable:{ Finally atEnd(...); try {...} catch(e) {...} }
(J'ai sorti le finaliseur du bloc try afin qu'il s'exécute après les blocs catch.)Vous pouvez utiliser un modèle "trap" - même si vous ne souhaitez pas utiliser le bloc try / catch.
Placez un objet simple dans la portée requise. Dans le destructeur de cet objet, mettez votre logique "finale". Quoi qu'il en soit, lorsque la pile sera déroulée, le destructeur de l'objet sera appelé et vous obtiendrez votre bonbon.
la source
Eh bien, vous pourriez trier par vous-même
finally
, en utilisant Lambdas, ce qui donnerait ce qui suit pour compiler correctement (en utilisant un exemple sans RAII bien sûr, pas le plus beau morceau de code):Voir cet article .
la source
Je ne suis pas sûr d’être d’accord avec les affirmations que RAII est un sur-ensemble de
finally
. Le talon d’Achille de RAII est simple: exceptions. La RAII est implémentée avec des destructeurs, et il est toujours faux en C ++ de se débarrasser d'un destructeur. Cela signifie que vous ne pouvez pas utiliser RAII lorsque vous avez besoin de votre code de nettoyage. Enfinally
revanche, si elles étaient mises en œuvre, il n’y aurait aucune raison de penser qu’il serait illégal de lancer à partir d’unfinally
bloc.Considérons un chemin de code comme ceci:
Si nous avions
finally
nous pourrions écrire:Mais il n’ya aucun moyen, que je puisse trouver, d’obtenir un comportement équivalent en utilisant RAII.
Si quelqu'un sait comment faire cela en C ++, la réponse m'intéresse beaucoup. Je serais même satisfait de quelque chose qui s'appuie, par exemple, sur l'application de toutes les exceptions héritées d'une même classe avec des capacités spéciales ou autres.
la source
complex_cleanup
peut lancer, vous pouvez avoir un cas où deux exceptions non capturées volent en même temps, comme vous le feriez avec RAII / destructors, et que C ++ refuse de permettre cela. Si vous souhaitez que l'exception d'origine soit visible, vous devezcomplex_cleanup
empêcher toute exception, comme ce serait le cas avec RAII / destructors. Si vous souhaitez quecomplex_cleanup
l’exception soit vue, alors je pense que vous pouvez utiliser des blocs imbriqués try / catch - bien qu’il s’agisse d’une tangente et qu’il est difficile de s’intégrer dans un commentaire, elle mérite donc une question distincte.finally
bloc putatif fonctionnerait clairement de la même manière qu'un jet dans uncatch
bloc - exceptions en vol WRT - ne pas appelerstd::terminate
. La question est "pourquoi nonfinally
en C ++?" et toutes les réponses disent "vous n'en avez pas besoin ... RAII FTW!" Ce que je veux dire, c’est que oui, RAII convient pour des cas simples comme la gestion de la mémoire, mais jusqu’à ce que le problème des exceptions soit résolu, il faut trop de réflexion / de travail en profondeur / de préoccupation / de conception pour devenir une solution polyvalente.