Le destructeur est-il appelé si le constructeur lance une exception?

Vous cherchez une réponse pour C # et C ++. (en C #, remplacez ‘destructor’ par ‘finalizer’)

Préambule: Herb Sutter a un excellent article sur le sujet:

http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-cc-and-java/

C ++: oui et non

Bien qu’un destructeur d’object ne soit pas appelé si son constructeur lève (l’object n’a jamais existé), les destructeurs de ses objects internes pourraient être appelés.

En résumé, toutes les parties internes de l’object (à savoir les objects membres) auront leurs destructeurs appelés dans l’ordre inverse de leur construction. Tout ce qui est construit à l’intérieur du constructeur n’aura pas son destructeur appelé à moins que RAII ne soit utilisé d’une certaine manière.

Par exemple:

struct Class { Class() ; ~Class() ; Thing * m_pThing ; Object m_aObject ; Gizmo * m_pGizmo ; Data m_aData ; } Class::Class() { this->m_pThing = new Thing() ; this->m_pGizmo = new Gizmo() ; } 

L’ordre de création sera:

  1. m_aObject aura son constructeur appelé.
  2. m_aData aura son constructeur appelé.
  3. Le constructeur de classe est appelé
  4. Dans le constructeur de classe, m_pThing aura son nouveau constructeur, puis son constructeur.
  5. Dans le constructeur de classe, m_pGizmo aura son nouveau constructeur, puis son constructeur.

Disons que nous utilisons le code suivant:

 Class pClass = new Class() ; 

Quelques cas possibles:

  • Si m_aData lance à la construction, m_aObject aura son destructeur appelé. Ensuite, la mémoire allouée par “new Class” est désallouée.

  • Si m_pThing lance un nouveau Thing (mémoire insuffisante), m_aData, puis m_aObject verront leurs destructeurs appelés. Ensuite, la mémoire allouée par la nouvelle classe est libérée.

  • Si m_pThing lance à la construction, la mémoire allouée par “new Thing” sera libérée. Ensuite, m_aData, puis m_aObject auront leurs destructeurs appelés. Ensuite, la mémoire allouée par la nouvelle classe est libérée.

  • Si m_pGizmo lance à la construction, la mémoire allouée par “new Gizmo” sera libérée. Ensuite, m_aData, puis m_aObject auront leurs destructeurs appelés. Ensuite, la mémoire allouée par la nouvelle classe est libérée. Notez que m_pThing a fui

Si vous souhaitez offrir la garantie d’exception de base, vous ne devez pas faire de fuite, même dans le constructeur. Ainsi, vous devrez écrire de cette manière (en utilisant STL, ou même Boost):

 struct Class { Class() ; ~Class() ; std::auto_ptr m_pThing ; Object m_aObject ; std::auto_ptr m_pGizmo ; Data m_aData ; } Class::Class() : m_pThing(new Thing()) , m_pGizmo(new Gizmo()) { } 

Ou même:

 Class::Class() { this->m_pThing.reset(new Thing()) ; this->m_pGizmo.reset(new Gizmo()) ; } 

si vous voulez / devez créer ces objects dans le constructeur.

De cette façon, peu importe où le constructeur lance, rien ne sera divulgué.

Il fait pour C # (voir le code ci-dessous) mais pas pour C ++.

 using System; class Test { Test() { throw new Exception(); } ~Test() { Console.WriteLine("Finalized"); } static void Main() { try { new Test(); } catch {} GC.Collect(); GC.WaitForPendingFinalizers(); } } 

Cette estampes “Finalized”

Le destructeur de la classe en cours de construction n’est pas appelé, car l’object n’a jamais été entièrement construit.

Cependant, le destructeur de sa classe de base (le cas échéant) est appelé, car l’object a été construit jusqu’à devenir un object de classe de base.

De plus, toutes les variables membres auront leurs destructeurs appelés (comme d’autres l’ont noté).

NB: ceci s’applique à C ++

En C ++, la réponse est no – le destructeur de l’object n’est pas appelé.

Cependant, les destructeurs de toutes les données de membre sur l’object seront appelés, sauf si l’exception a été levée lors de la construction de l’une d’ entre elles .

Les données de membre en C ++ sont initialisées (c’est-à-dire construites) dans le même ordre que la déclaration. Ainsi, lorsque le constructeur lancera, toutes les données de membres initialisées – soit explicitement dans la liste d’initialisation des membres – seront supprimées. à nouveau dans l’ordre inverse.

Si le constructeur ne termine pas son exécution, l’object n’existe pas, il n’y a donc rien à détruire. Ceci est en C ++, je n’ai aucune idée de C #.

C ++ –

Nan. Le destructeur n’est pas appelé pour des objects partiellement construits. Une mise en garde: le destructeur sera appelé pour ses objects membres qui sont complètement construits. (Comprend les objects automatiques et les types natifs)

BTW – Ce que vous cherchez vraiment s’appelle “Stack Unwinding”

Ne faites pas les choses qui provoquent des exceptions dans le constructeur.

Appelez une initialisation () après le constructeur qui peut générer des exceptions.

Pour C ++, ceci est traité dans une question précédente: le code ci-dessous provoquera-t-il une fuite de mémoire dans c ++?

Comme en C ++ quand une exception est lancée dans un constructeur, le destructeur n’est pas appelé, mais les dtors des membres de l’object (qui ont été construits) sont appelés, ceci est la raison principale d’utiliser des objects pointeurs intelligents un bon moyen de prévenir les memory leaks dans une situation comme celle-ci.