11
votes

Destructeur qui appelle une fonction qui peut lancer une exception en C ++

Je sais que je ne devrait pas jeter exceptions d'un destructeur.

Si mon destructeur appelle une fonction qui peut lancer une exception, est-ce que ça va si je l'attrape dans le destructeur et que je ne le jette pas plus loin? Ou peut-il causer de toute façon et je ne devrais pas appeler de telles fonctions d'un destructeur du tout?


2 commentaires

Juste pour des éclaircissements, demandez-vous s'il est correct si le destructeur capte l'exception, il ne quitte donc jamais le destructeur, ou s'il est correct de le laisser quitter le destructeur tant qu'il est pris à l'extérieur?


Je demande si ça va si l'exception reste dans le Tor.


5 Réponses :


20
votes

Oui, c'est légal. Une exception ne doit pas échapper du destructeur, mais tout ce qui se passe à l'intérieur du destructeur, ou dans les fonctions, c'est à vous de décider.

(Techniquement, une exception peut également échapper à un appel destructeurs. Si cela se produit pendant la pile de déroulement, car une autre exception a été lancée, std :: terminé est appelé. Donc, il est donc bien défini par la norme, mais c'est un vraiment mauvaise idée.)


12 commentaires

Désolé pour le nitpicking, mais j'utiliserais un terme autre que "légal". Lancer une exception dans le destructeur est également "légal", i. e. Il va compiler et courir. Mais c'est une mauvaise pratique qui causera des effets désagréables.


Oui et non. Vous avez raison, il est techniquement légal de jeter une exception à partir d'un destructeur ( std :: terminer est appelé alors). Mais votre définition de "légal" est faux. Juste parce que quelque chose compile et ne le rend pas légal c ++.


Je ne suis pas sûr de ce que vous voulez que le légal signifie, mais je suis avec DIMA sur celui-ci. Bien que C ++ n'utilise pas le travail juridique, il définit un programme bien formé (probablement le plus proche de ce que je pense comme «légal»), comportement non spécifié et comportement non défini . Permettre à une exception de dépendre d'un destructeur peut se produire dans un programme bien formé et le comportement n'est ni non défini, ni non spécifié. Cela ne l'empêche pas d'être un comportement presque universellement indésirable.


Oh, je suis d'accord avec son point que jetant une exception d'un destructeur est bien défini. Je veux dire que "il compile et exécute" n'est pas la même chose que "légal" par une définition sane du mot.


Oh, c'est vrai, oui, je suis vraiment d'accord avec ça, bien que je sois aussi rapide à venir à l'opinion que le mot "légal" n'est pas très utile de W.R.T. C ++.


vrai, et j'aurais probablement besoin d'utiliser un autre terme (plus spécifique).


Il n'y a absolument aucun problème avec une exception qui échappe à un destructeur dans des conditions normales (STD :: Terminate () n'est pas appelée). Ce n'est qu'un problème si une autre exception est déjà appropriée (STD :: Terminate () est appelée), c'est parce que le temps d'exécution ne peut pas gérer le concept de deux exceptions parallèles propogrant simultanément (ou les concepteurs C ++ ne pouvaient penser à une manière logique. gérer la situation).


Ce principe est également connu comme ce qui se passe dans les restes destructeurs à destructeurs.


-1 Pour tromper comme si STD :: Terminate () est appelé si une exception laisse destructeur. En effet, STD :: Terminate () est appelé si la pile déroulant est également en cours. Sinon, une exception ne quitte que le destructeur et "l'opérateur supprime" ne s'appelle pas (il y a donc une fuite de mémoire pour les objets, alloué sur le tas).


@Sergerogatch "tromper?" Vous voulez dire que c'est un mensonge intentionnel de tromper les gens? Qu'est ce qui te fait penser ça?


@jalf, non, je ne veux pas dire intentionnel ou involontalement parce que je ne sais pas. L'anglais n'est pas ma langue maternelle. Peut-être serait-il préférable de dire "trompeur" ou "donnant une mauvaise idée", mais c'est beaucoup plus à penser ou même à taper. Désolé pour les inconvénients. Je vois que vous avez édité votre réponse afin que je puisse maintenant supprimer mon -1.


@Sergerogatch Pas de problème :) Et oui, trompeur serait un meilleur mot. :) (et merci d'avoir souligné l'erreur.)



-1
votes

Vous pouvez trouver Cette page de C ++ FAQ Lite pour être informatif. La réponse de base est: "Ne le faites pas." Où envisagez-vous exactement d'attraper cette exception? Tout ce que vous envisagez de faire lorsque vous attrapez cette exception, faites-le simplement avec un appel de fonction à la place ou quelque chose (par exemple, logez-le ou définissez un drapeau pour avertir l'utilisateur ou quoi que ce soit). Lancer des exceptions du destructeur exécute le risque de mettre fin à votre programme complet.


1 commentaires

Comment est-ce pertinent pour la question? S'il appelle une fonction qui peut lancer std :: Bad_alloc, quelle serait l'alternative? Écrivez une fonction "wrapper"? Ce n'est pas une solution, qui blesse juste l'exception dans une autre couche.



0
votes

Réponse simple, n'autorise jamais une exception d'un DTOR!

La réponse compliquée. Vous obtenez seulement vraiment cloué si l'exception échappe au DTOR pendant qu'une autre exception est active. Le cas normal pour cela est lorsque vous vous détournez déjà la pile d'une autre exception et que l'objet en question est détruit. Dans ce cas si l'exception échappe à la DTOR, alors std :: terminez est appelé, note que vous pouvez mettre dans votre propre gestionnaire pour std :: terminer en appelant std :: set_terminate . La mise en œuvre par défaut de std :: terminer est d'appeler Abort.

Compliquer les choses plus, la plupart des fonctions qui souhaitent faire toute garantie sur leur sécurité d'exception, principalement la garantie de base ou la forte garantie, dépendent des types sous-jacents à eux-mêmes ne pas jeter dans leur DTor *

La vraie question est, quel état serait votre programme lorsque cette erreur se produit? Comment pouvez-vous récupérer? Où cette récupération devrait-elle être traitée? Vous devez examiner votre cas spécifique et travailler ces problèmes. Parfois, c'est bien d'attraper l'exception et de l'ignorer. D'autres fois, vous devez élever des drapeaux rouges.

donc la réponse est: elle a permis de jeter une exception dans un DTor, mais vous ne devriez jamais lui permettre de s'échapper.

* Voici un bref Synopsis de l'exception garantit (voici beaucoup plus long < Un href = "http://www.boost.org/community/exception_safety.html" rel = "Nofollow Noreferrer"> Article )

  1. récapitulatif: définir brièvement les garanties de sécurité d'exception Abrahams (base, fort, et nothow).

    La garantie de base est celle qui a échoué Les opérations peuvent altérer l'état du programme, mais aucune fuite ne se produit et affectée Les objets / modules sont toujours destructibles et utilisable, dans un cohérent (mais pas Etat nécessairement prévisible).

    La forte garantie implique COMMITES DE TRANSACTION / ROLLBACK Semantitique: garantie des opérations ayant échoué L'état du programme est inchangé avec respect des objets opérés sur. Cela signifie aucun effet secondaire qui affecte les objets, y compris la validité ou Contenu des objets d'assistance connexes comme les itérateurs pointant dans Les conteneurs sont manipulés.

    la garantie de nohrow signifie que Les opérations ayant échoué ne se produiront pas. Les opération ne lancera pas une exception.


1 commentaires

Si je comprends la question, il ne pose pas de questions sur des exceptions laissant le dtteur du tout, mais il est simplement de savoir si le DTOR appelle une fonction qui jette une exception, tant que le DTOR attire l'exception afin qu'elle ne se propage pas.



4
votes

Oui.

Regardez la classe STD :: FStream dans la bibliothèque standard pour un exemple. p>

  • Fermer () pourrait potentiellement jeter une exception. li>
  • Le destroctor peut appeler près () mais le destructeur ne jette pas (il avalera des exceptions). Li> ul>

    Le concept est que si le destructeur appelle toutes les méthodes pouvant lancer, ces méthodes doivent être publiques. Ainsi, si l'utilisateur de votre objet souhaite vérifier les exceptions, ils peuvent utiliser les méthodes publiques et gérer l'exception. S'ils ne se soucient pas de l'exception, laissez simplement le destructeur gérer le problème. P>

    retour à l'exemple STD :: FRStream exemple. P>

    {
        std::fstream   text("Plop");
        // Load Text.
    
        // I don't care if the close fails.
        // So let the destructor handle it and discard exceptions
    }
    
    
    
    {
        // If this fails to write I should at least warn the user.
        // So in this case I will explicitly try and close it.
        try
        {
            std::ofstram    password("/etc/password");
            // Update the password file.
    
            password.close();
        }
        catch(...)
        {
              Message.ShowDialog("You failed to update the Password File");
        }
    }
    


0 commentaires

2
votes

Vous pouvez trouver quelques exemples ici: https://software.intel.com/sites/products/documentation/doclib/iss/2013/sa-ptr/sa-ptr_win_lin/guid-d2983b74- 74e9-4868-90E0-D65A80F8F69F.HTM

Si une exception laisse destructeurs destructeurs pendant la pile de détend d'une autre exception en cours de propagation, alors STD :: Terminate () est appelée.

Lorsqu'aucune pile déroulant n'est en cours, une exception peut laisser destructeur sans STD :: Terminez () être appelé. Toutefois, pour les objets alloués sur le tas, cela entraînera une fuite de mémoire, car "l'opérateur Supprimer" ne sera pas appelé à l'objet qui jette une exception hors de ses destructeurs. Étonnamment, le destructeur de la classe de base est encore appelé dans ce cas: Qu'advient-il de la destructeur de classe de base si un destructeur de classe dérivé jette une exception

Si l'exception est attrapée à l'intérieur du destructeur (de sorte que l'exception ne quitte pas le destructeur), aucun problème, même si la pile ne se déroule d'une autre exception est en cours. Ce cas est décrit plus profondément ici: http: //bin-login.name/ftp/pub/docs/programming_languages/cpp/cffective_cpp/mec/mi11_fr.htm


0 commentaires