Is the destructor called if the constructor throws an exception?
Asked Answered
F

8

52

Looking for an answer for C# and C++. (in C#, replace 'destructor' with 'finalizer')

Frisian answered 9/10, 2008 at 19:1 Comment(3)
I think the thing to do here is set up a throw-away project (or two: one per langauge) and do a check to find out.Reconstructionism
The joy of compiling from the command-line is you don't need to set up a project. Just load up a file in a text editor, compile very easily, and run :)Assets
@loyc-etc, C++ and C# are pretty different in their semantics. This is really two separate questions.Tellurium
K
56

Preamble: Herb Sutter has a great article on the subject:

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

C++ : Yes and No

While an object destructor won't be called if its constructor throws (the object "never existed"), the destructors of its internal objects could be called.

As a summary, every internal parts of the object (i.e. member objects) will have their destructors called in the reverse order of their construction. Every thing built inside the constructor won't have its destructor called unless RAII is used in some way.

For example:

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() ;
}

The order of creation will be:

  1. m_aObject will have its constructor called.
  2. m_aData will have its constructor called.
  3. Class constructor is called
  4. Inside Class constructor, m_pThing will have its new and then constructor called.
  5. Inside Class constructor, m_pGizmo will have its new and then constructor called.

Let's say we are using the following code:

Class pClass = new Class() ;

Some possible cases:

  • Should m_aData throw at construction, m_aObject will have its destructor called. Then, the memory allocated by "new Class" is deallocated.

  • Should m_pThing throw at new Thing (out of memory), m_aData, and then m_aObject will have their destructors called. Then, the memory allocated by new Class is deallocated.

  • Should m_pThing throw at construction, the memory allocated by "new Thing" will be deallocated. Then m_aData, and then m_aObject will have their destructors called. Then, the memory allocated by new Class is deallocated.

  • Should m_pGizmo throw at construction, the memory allocated by "new Gizmo" will be deallocated. Then m_aData, and then m_aObject will have their destructors called. Then, the memory allocated by new Class is deallocated. Note that m_pThing leaked

If you want to offer the Basic Exception Guarantee, you must not leak, even in the constructor. Thus, you'll have to write this this way (using STL, or even 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())
{
}

Or even:

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

if you want/need to create those objects inside the constructor.

This way, no matter where the constructor throws, nothing will be leaked.

Knight answered 9/10, 2008 at 19:43 Comment(7)
Another scenario, should m_pGizmo throw at new, m_pThing still leaks right? Thanks..Emalee
No, because m_pThing is a smart pointer. If m_pThing did construct correctly, its destructor will be called if m_pGizmo throws.Knight
Herb Sutter's article is wrong for C# and Java. The finalizers appear to be run even if the constructors throw an exception.Kliman
@David Leonard: You're wrong for C#. I'm not a C# lawyer, but with some Googling, I found the following article which says the construction of a C# object is OUTSIDE the implicit try/finally of the "using" statement. Thus, the Dispose method is NOT called. See: codeproject.com/KB/cs/tinguusingstatement.aspx and msdn.microsoft.com/en-us/library/aa664736.aspxKnight
@David Leonard: You're ALMOST wrong for Java, as Herb Sutter's article places the construction of the object outside the try/finally, doing manually in Java what is automated in C#. I guess you could declare the reference outside, and write the new inside the try/finally, but there must be some reason why it is not done. A little research, though, will reveal that one should avoid using finalize() in Java because its execution can't be relied upon. I am not a Java lawyer, so I have no fast answer.Knight
@Knight Dispose is not the same thing as a finalizer.Selima
@Selima : You're right. As Sutter's article is about disposers, not finalizers,when David Leonard was writing "Sutter's article was wrong, The finalizers appear to be run even if the constructors throw an exception", I wrongly assumed he messed up names (thus, my answer about C# disposers, or Java's finally clause in Sutter's example). The good thing is that your comment gave me the occasion to read the ulterior comments on Sutter's about finalizer executing even when the constructors throws: Another reason to not use them...Knight
A
58

It does for C# (see code below) but not for C++.

using System;

class Test
{
    Test()
    {
        throw new Exception();
    }

    ~Test()
    {
        Console.WriteLine("Finalized");
    }

    static void Main()
    {
        try
        {
            new Test();
        }
        catch {}
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

This prints "Finalized"

Assets answered 9/10, 2008 at 19:7 Comment(5)
This may be true for C#, but not C++.Surround
I thought ~Test was a finalizer in C#, not a destructor? Ah I see the ~ syntax is sugar for setting up a finalizer. In the case that my ctor could throw, I expect I'd want to manually add the finalizer as the last step of the ctor to avoid cleaning up non-existent messes.Adrenaline
@Logan: It depends on which version of the C# spec you read as to whether they call it "finalizer" or "destructor".Assets
@Jon: Is there any other C# specification than Ecma-334? I know they exist, in fact they are published on your website ( csharpindepth.com/articles/chapter1/Specifications.aspx ) I mean ... Too bad there are no Standard specifications for versions higher than 2.0. That said (protest) ... The semantic of C# Finalizer is not like C++ Destructor (Obviously, you know it). In the C# example I think more appropriate to replace the Finalizer for a Disposer.Percyperdido
@FernandoPelliccioni: No, there isn't a later version of the ECMA spec than that. As for disposal - the question specifically asks about a finalizer. Changing it to use disposal wouldn't answer the question.Assets
K
56

Preamble: Herb Sutter has a great article on the subject:

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

C++ : Yes and No

While an object destructor won't be called if its constructor throws (the object "never existed"), the destructors of its internal objects could be called.

As a summary, every internal parts of the object (i.e. member objects) will have their destructors called in the reverse order of their construction. Every thing built inside the constructor won't have its destructor called unless RAII is used in some way.

For example:

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() ;
}

The order of creation will be:

  1. m_aObject will have its constructor called.
  2. m_aData will have its constructor called.
  3. Class constructor is called
  4. Inside Class constructor, m_pThing will have its new and then constructor called.
  5. Inside Class constructor, m_pGizmo will have its new and then constructor called.

Let's say we are using the following code:

Class pClass = new Class() ;

Some possible cases:

  • Should m_aData throw at construction, m_aObject will have its destructor called. Then, the memory allocated by "new Class" is deallocated.

  • Should m_pThing throw at new Thing (out of memory), m_aData, and then m_aObject will have their destructors called. Then, the memory allocated by new Class is deallocated.

  • Should m_pThing throw at construction, the memory allocated by "new Thing" will be deallocated. Then m_aData, and then m_aObject will have their destructors called. Then, the memory allocated by new Class is deallocated.

  • Should m_pGizmo throw at construction, the memory allocated by "new Gizmo" will be deallocated. Then m_aData, and then m_aObject will have their destructors called. Then, the memory allocated by new Class is deallocated. Note that m_pThing leaked

If you want to offer the Basic Exception Guarantee, you must not leak, even in the constructor. Thus, you'll have to write this this way (using STL, or even 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())
{
}

Or even:

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

if you want/need to create those objects inside the constructor.

This way, no matter where the constructor throws, nothing will be leaked.

Knight answered 9/10, 2008 at 19:43 Comment(7)
Another scenario, should m_pGizmo throw at new, m_pThing still leaks right? Thanks..Emalee
No, because m_pThing is a smart pointer. If m_pThing did construct correctly, its destructor will be called if m_pGizmo throws.Knight
Herb Sutter's article is wrong for C# and Java. The finalizers appear to be run even if the constructors throw an exception.Kliman
@David Leonard: You're wrong for C#. I'm not a C# lawyer, but with some Googling, I found the following article which says the construction of a C# object is OUTSIDE the implicit try/finally of the "using" statement. Thus, the Dispose method is NOT called. See: codeproject.com/KB/cs/tinguusingstatement.aspx and msdn.microsoft.com/en-us/library/aa664736.aspxKnight
@David Leonard: You're ALMOST wrong for Java, as Herb Sutter's article places the construction of the object outside the try/finally, doing manually in Java what is automated in C#. I guess you could declare the reference outside, and write the new inside the try/finally, but there must be some reason why it is not done. A little research, though, will reveal that one should avoid using finalize() in Java because its execution can't be relied upon. I am not a Java lawyer, so I have no fast answer.Knight
@Knight Dispose is not the same thing as a finalizer.Selima
@Selima : You're right. As Sutter's article is about disposers, not finalizers,when David Leonard was writing "Sutter's article was wrong, The finalizers appear to be run even if the constructors throw an exception", I wrongly assumed he messed up names (thus, my answer about C# disposers, or Java's finally clause in Sutter's example). The good thing is that your comment gave me the occasion to read the ulterior comments on Sutter's about finalizer executing even when the constructors throws: Another reason to not use them...Knight
L
11

The destructor of the class still being constructed is not called, because the object was never fully constructed.

However, the destructor of its base class (if any) IS called, because the object was constructed as far as being a base class object.

Moreover, any member variables will have their destructors called too (as others have noted).

NB: this applies to C++

Lucaslucca answered 9/10, 2008 at 20:6 Comment(1)
"destructors of all base classes", to be pedantic. The standard has a whole lot of words how it works in MI when one ctor throws.Chelseychelsie
S
2

In C++, the answer is no - object's destructor is not called.

However, the destructors of any member data on the object will be called, unless the exception was thrown while constructing one of them.

Member data in C++ is initialized (i.e. constructed) in the same order as it is declared, so when the constructor throws, all member data that has been initialized - either explicitly in the Member Initialization List (MIL) or otherwise - will be torn down again in reverse order.

Surround answered 9/10, 2008 at 19:14 Comment(3)
But it should be noted that a raw pointer member which may have been initialized using new will not be automatically deleted.Prohibitive
He's quite correct actually. If you have a T* member initialized with new T, then you actually have two objects: the new'ed T and the T* itself. Like an int, a T* has no destructor. Hence, when the T* member is deleted, the T pointed to is not.Chelseychelsie
Agreed. The task of deleting that pointer normally belongs in the destructor, and since the destructor is not called, there's a memory leak (unless a try/catch is written within the constructor to take care of this). The safer alternative is to use auto pointers or smart pointers.Surround
U
1

If the constructor doesn't finish executing, the object doesn't exist, so there's nothing to destruct. This is in C++, I have no idea about C#.

Unfleshly answered 9/10, 2008 at 19:8 Comment(0)
P
1

For C++ this is addressed in a previous question: Will the below code cause memory leak in c++

Since in C++ when an exception is thrown in a constructor the destructor does not get called, but dtors for the object's members (that have been constructed) do get called, this is a primary reason to use smart pointer objects over raw pointers - they are a good way to prevent memory leaks in a situation like this.

Prohibitive answered 9/10, 2008 at 21:26 Comment(0)
F
0

C++ -

Nope. Destructor is not called for partially constructed objects. A Caveat: The destructor will be called for its member objects which are completely constructed. (Includes automatic objects, and native types)

BTW - What you're really looking for is called "Stack Unwinding"

Frequentative answered 9/10, 2008 at 19:13 Comment(1)
Actually, I'm writing an STL-style collection, so I call constructors and destructors manually. Therefore I was wondering whether it's normal to call the destructor in case of an exception. But this "partial destruction" you describe must be done by the compiler, so I don't have to.Frisian
C
0

Don't do things that cause exceptions in the constructor.

Call an Initialize() after the constructor that can throw exceptions.

Cammi answered 9/10, 2008 at 19:16 Comment(5)
Please, could you explain why? The "Initialize()" idiom is an antipattern in most cases in C++. Perhaps you're mistaking with "don't ever throw an exception from a Destructor?"Knight
I know, designing a class with "Initialize()" beats the purpose of having a class at all.Bridwell
There are plenty of valid reasons for creating classes with an init method, especially when dealing with concurrency. Sometimes you need object creation to be cheap, even when the primary initialization of that object is expensive.Unhorse
Google's code guidelines discourage using exceptions at all, so they discourage throwing from the constructor ( google-styleguide.googlecode.com/svn/trunk/… ).Bullwhip
On the other hand, pretty much everybody else says to throw from a constructor because that is the only way to signal the constructor failed ( herbsutter.wordpress.com/2008/07/25/… ).Bullwhip

© 2022 - 2024 — McMap. All rights reserved.