55 votes

Pourquoi le destructeur n'est-il pas appelé en cas d'exception ?

Je m'attendais à A::~A() dans ce programme, mais ce n'est pas le cas :

#include <iostream>

struct A {
  ~A() { std::cout << "~A()" << std::endl; }
};

void f() {
  A a;
  throw "spam";
}

int main() { f(); }

Toutefois, si je remplace la dernière ligne par

int main() try { f(); } catch (...) { throw; }

entonces A::~A() est appelé.

Je compile avec "Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86" depuis Visual Studio 2005. La ligne de commande est la suivante cl /EHa my.cpp .

Le compilateur a-t-il raison comme d'habitude ? Que disent les normes à ce sujet ?

1 votes

Pour information, j'ai reproduit ce même problème avec le même code dans Visual C++ 2003. +1 pour la question.

74voto

lefticus Points 1420

Le destructeur n'est pas appelé parce que terminate() pour l'exception non gérée est appelé avant que la pile ne soit déroulée.

Je ne connais pas les détails spécifiques de la spécification C++, mais une trace de débogage avec gdb et g++ semble le confirmer.

Selon la projet de norme section 15.3, point 9 :

9 If no matching handler is found in a program, the function terminate()
  (\_except.terminate\_)  is  called.  Whether or not the stack is unwound
  before calling terminate() is implementation-defined.

2 votes

Upvote pour la référence au document officiel de la norme.

18voto

Alex Che Points 822

La spécification du langage C++ stipule : Le processus d'appel des destructeurs pour les objets automatiques construits sur le chemin allant d'un bloc try à une expression throw est appelé "déroulement de la pile". Votre code original ne contient pas de bloc try, c'est pourquoi le déroulement de la pile ne se produit pas.

1 votes

La réponse sélectionnée mentionne terminate qui est la raison ultime. Cependant, je pense que cette réponse est plus proche de la bonne. Si l'on n'utilise pas try Il semble que le déroulement de la pile n'ait pas lieu et que le destructeur ne soit pas appelé. Mon expérience semble suggérer que le destructeur est appelé lorsque l'erreur correcte a été détectée, avant le bloc de code suivant catch est en cours.

3voto

Loki Astari Points 116129

Désolé, je n'ai pas de copie de la norme sur moi.
J'aimerais vraiment avoir une réponse définitive à cette question, si quelqu'un qui a une copie de la norme veut bien partager les chapitres et les versets de ce qui se passe :

D'après ce que j'ai compris, le terme n'est appelé que si :

  • Le mécanisme de gestion des exceptions ne trouve pas de gestionnaire pour une exception lancée.
    Les cas suivants sont plus spécifiques :
    • Pendant le déroulement de la pile, une exception échappe à un destructeur.
    • Une expression lancée, une exception échappe au constructeur.
    • Une exception échappe au constructeur/destructeur d'un objet statique non local (c'est-à-dire global).
    • Une exception échappe à une fonction enregistrée avec atexit().
    • Une exception échappe à main()
  • Essayer de relancer une exception alors qu'aucune exception n'est en cours de propagation.
  • Une exception inattendue échappe à une fonction contenant des spécificateurs d'exception (via unexpected)

2voto

James Curran Points 55356

Dans le second exemple, le dtor est appelé lorsqu'il quitte le bloc try{}.

Dans le premier exemple, le dtor est appelé lorsque le programme se termine après avoir quitté la fonction main() --- à ce moment-là, cout peut déjà avoir été détruit.

1 votes

Non, c'est faux. Le destructeur est garanti d'être appelé avant que le programme ne quitte l'ordinateur. main . Les cout est garanti d'être appelé après .

1 votes

Correction : le destructeur de a doit être appelé avant f reste !

2 votes

De plus, le programme ne quitte jamais la fonction principale dans le premier exemple. Il est interrompu via terminate with par une "exception inattendue".

2voto

Klaim Points 24511

Je suppose aussi que le compilateur ne génère pas de code relative à la "une" comme il n'est pas référencé, mais encore, ce n'est pas le comportement que le destructeur n'quelque chose qui doivent être exécutées.

Donc, j'ai essayé dans VS2008/vc9 (+SP1), Debug et Release et ~A est appelé après que l'exception est levée, sortir de f() - c'est le bon comportement, si je suis à droite.

Maintenant, j'ai juste essayé avec VS2005/vc8 (+SP1) et c'est le même comportement.

J'ai utilisé des points d'arrêt pour être sûr. Je viens de vérifier avec la console et j'ai le "~" message trop. Peut-être que vous l'avez fait mal quelque part d'autre?

1 votes

J'ai créé un fichier texte avec le premier exemple (sans essai), j'ai ouvert "V cl /EHa my.cpp . Résultat de l'exécution : Cette application a demandé au Runtime de la terminer d'une manière inhabituelle. Veuillez contacter l'équipe de support de l'application pour plus d'informations.

1 votes

Je ne suis pas familier avec les paramètres de compilation en ligne de commande, mais je suppose que c'est similaire au mode Release où s'il n'y a pas de try/catch le code n'ira pas plus loin que l'instruction throw (c'est ce qui se passe quand j'essaie) et l'application va juste "planter" (c'est le comportement voulu).

1 votes

Après avoir regardé les listes d'assemblage, je pense que les optimisations sont désactivées par défaut. C'est donc plus proche de Debug. BTW, voir le commentaire de paercebal, il a réussi à reproduire avec VS2003.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X