10
votes

Tranchement d'exception - est-ce dû au constructeur de copie généré?

Je viens de corriger un bogue très subtil dans notre code, causé par la décharge d'une exception, et je veux maintenant m'assurer que je comprends exactement ce qui se passait.

Voici notre classe d'exception de base, une classe dérivée, une classe dérivée, et fonctions pertinentes: xxx

Le bogue était bien sûr que OuterCall finit par lancer une exception, au lieu d'un dérivé. Mon bug résultait de plus haut dans la pile d'appels tente d'attraper l'échec dérivé.

Maintenant, je veux juste m'assurer que je comprends - je crois que lors de la ligne "Three e", un nouvel objet d'exception est être créé, en utilisant un constructeur de copie par défaut. Est-ce ce qui se passe vraiment?

Si oui, suis-je autorisé à verrouiller des constructeurs de copies pour des objets qui seront lancés? Je préférerais vraiment que cela ne se reproduisait plus, et notre code n'a aucune raison de copier des objets d'exception (que je connais de).

S'il vous plaît, aucun commentaire sur le fait que nous avons notre propre hiérarchie d'exception. C'est un peu de vieille design que je travaille pour corriger (je fais de bons progrès. Je me suis débarrassé de la classe de cordes à domicile et de nombreux conteneurs adultes.)

Mise à jour: Pour être claire, j'avais corrigé le bogue (en changeant 'lancer E' pour "lancer") avant que j'ai jamais posé la question. Je cherchais juste une confirmation de ce qui se passait.


4 commentaires

Je sais que vous avez dit de ne pas vous inquiéter de l'autre affaire, mais seulement deux autres choses: faites votre exception Classe hériter de std :: exception et attraper des choses par const & .


Quel est l'avantage d'attraper comme const? Je sais toujours par référence, mais pourquoi const? (Pas que je puisse faire cela avec cette classe en ce moment, mais un jour ...)


@KOHNE: L'avantage de Cons dans ce contexte est similaire à celui de tout autre paramètre - vous spécifiez clairement l'intention que vous n'avez pas l'intention de changer l'état de l'objet et que le compilateur doit s'assurer que vous ne le faites pas - l'idée Derrière Cons-étant que les objets immuables sont plus faciles à raisonner (c'est-à-dire le débogage, le test, le code de prouve correct) car ils aident à écrire un code transparent de manière référentielle


Ok, donc juste les avantages normaux. Je m'assurais bien qu'il n'y avait pas quelque chose de subtil et spécifique aux exceptions que je ne savais pas. Merci!


4 Réponses :


22
votes

Lorsque vous lancez un objet, vous jetez une copie de l'objet, pas l'original. Pensez-y - l'objet d'origine est sur la pile, mais la pile est déroutée et invalidée.

Je crois que cela fait partie de la norme, mais je n'ai pas de copie à faire référence.

Le type d'exception étant jeté dans le bloc de capture est le type de base de la capture, pas le type de l'objet qui a été lancé. Le moyen de contourner ce problème est de lancer; plutôt que lancer e; qui lancera l'exception attrapée originale.


1 commentaires

Cela indiquerait que je ne peux pas jeter quelque chose qui n'a pas de constructeur de copie, alors? Ça a du sens.



10
votes

Un rapide Google suggère que oui, vous lancez le constructeur de copie est requis et doit être public. (Ce qui a du sens, comme vous initialisez une copie de E code> et jetant cela.)

Quoi qu'il en soit, il suffit d'utiliser jetez code> sans spécifier l'objet d'exception, à retenir quoi a été capturé dans le attrape code>. Cela ne devrait-il pas résoudre le problème? P>

  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw;
  }


1 commentaires

Je suis désolé, oui, j'ai déjà corrigé ce bug en faisant la réthute correctement. J'essayais d'obtenir la confirmation sur le constructeur de copie.



7
votes

Oui.

throw;


0 commentaires

1
votes

C ++ ne cesse jamais de m'étonner. J'aurais perdu beaucoup d'argent si c'était un pari sur quel était le comportement!

L'objet d'exception est d'abord copié à un temporaire et que vous auriez dû utiliser lancer . Pour citer la norme 15.1 / 3:

Une expression de lancement initialise un objet temporaire, appelé objet d'exception, dont le type est déterminé en supprimant les qualificateurs de CV de niveau supérieur du type statique de l'opérande de lancer et ajuster le type de "tableau de t "ou" fonction renvoyer t "sur" pointeur sur t "ou" pointeur pour fonctionner de retourner t ", respectivement.

Je pense que cela donne lieu à une règle standard de codage très utile:

Les classes de base d'une hiérarchie d'exception devraient avoir un puissant destructeur virtuel.

ou

Le constructeur de copie pour une classe de base dans une hiérarchie d'exception doit être protégé.

soit atteindre l'objectif que le compilateur mettra en garde lorsque vous essayez de "jeter E" car dans le premier cas, vous ne pouvez pas créer une instance d'une classe abstraite et la seconde car vous ne pouvez pas appeler le constructeur de copie.


0 commentaires