42 votes

Le destructeur est-il appelé si le constructeur lève une exception ?

Je cherche une réponse pour C# et C++. (en C#, remplacez 'destructor' par 'finalizer')

54voto

Jon Skeet Points 692016

Il le 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 impression "Finalisé"

51voto

paercebal Points 38526

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

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

C++ : Oui et non

Alors que le destructeur d'un objet ne sera pas appelé si son constructeur lâche l'affaire (l'objet n'a "jamais existé"), les destructeurs de ses objets internes peuvent être appelés.

En résumé, toutes les parties internes de l'objet (c'est-à-dire les objets membres) auront leur destructeur appelé dans l'ordre inverse de leur construction. Chaque chose construite à l'intérieur du constructeur n'aura pas son destructeur appelé à moins que RAII soit utilisé d'une manière ou d'une autre.

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 la création sera :

  1. Le constructeur de m_aObject sera appelé.
  2. Le constructeur de m_aData sera appelé.
  3. Le constructeur de la classe est appelé
  4. A l'intérieur du constructeur de la classe, m_pThing aura son constructeur new et then appelé.
  5. A l'intérieur du constructeur de la classe, m_pGizmo aura son constructeur new et then appelé.

Disons que nous utilisons le code suivant :

Class pClass = new Class() ;

Quelques cas possibles :

  • Si m_aData échoue à la construction, le destructeur de m_aObject sera appelé. Ensuite, la mémoire allouée par "new Class" est désallouée.

  • Si m_pThing lance une nouvelle Chose (sans mémoire), m_aData, puis m_aObject verront leurs destructeurs appelés. Ensuite, la mémoire allouée par new Class est désallouée.

  • Si m_pThing échoue à la construction, la mémoire allouée par "new Thing" sera désallouée. Ensuite, les destructeurs de m_aData, puis de m_aObject seront appelés. Ensuite, la mémoire allouée par new Class est désallouée.

  • Si m_pGizmo échoue à la construction, la mémoire allouée par "new Gizmo" sera désallouée. Ensuite, les destructeurs de m_aData, puis de m_aObject seront appelés. Ensuite, la mémoire allouée par new Class est désallouée. Notez que m_pThing a fui

Si vous voulez offrir la garantie de base des exceptions, vous ne devez pas fuir, même dans le constructeur. Ainsi, vous devrez écrire ceci de cette façon (en utilisant STL, ou même Boost) :

struct Class
{
   Class() ;
   ~Class() ;

   std::auto_ptr<Thing>   m_pThing ;
   Object                 m_aObject ;
   std::auto_ptr<Gizmo>   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 objets dans le constructeur.

Ainsi, quel que soit l'endroit où le constructeur est lancé, rien ne sera divulgué.

10voto

MarkR Points 37178

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

Cependant, le destructeur de sa classe de base (s'il existe) EST appelé, car l'objet a été construit jusqu'à être un objet de classe de base.

De plus, les destructeurs de toutes les variables membres seront également appelés (comme d'autres l'ont noté).

NB : ceci s'applique à C++

2voto

Matt Dillard Points 9040

En C++, la réponse est non - le destructeur de l'objet est no appelé.

Cependant, les destructeurs de toutes les données membres de l'objet se sera appelé, sauf si l'exception a été levée lors de la construction d'un des éléments suivants les .

En C++, les données des membres sont initialisées (c'est-à-dire construites) dans l'ordre dans lequel elles sont déclarées. Ainsi, lorsque le constructeur se lance, toutes les données des membres qui ont été initialisées - que ce soit explicitement dans la liste d'initialisation des membres (MIL) ou autrement - seront à nouveau détruites dans l'ordre inverse.

1voto

Greg Rogers Points 18119

Si le constructeur ne finit pas de s'exécuter, l'objet n'existe pas, donc il n'y a rien à destructurer. Ceci est en C++, je n'ai aucune idée sur le C#.

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