views:

3041

answers:

10

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

+30  A: 

Questions like this one honestly surprise me.

You have a compiler - find out for yourself.

Not to be mean, but this is exactly the kind of question where your best friend is not the internet, but your compiler.

The only reason I'd even think of asking the internet this kind of question is if I believed that different compilers behaved differently.

Matt Cruikshank
I teach a programming course, and this mindset is popular -- where in the book or the internet is the exact code I need to solve this problem? The idea of experimenting and trying things out is foreign. Maybe Google is making us stupid. http://www.theatlantic.com/doc/200807/google
JohnMcG
Err... Note that your viewpoint crumble to useless dust when your "experiment" falls on one compiler bug/peculiarity. The C++ standard have a very clear definition of what is supposed to happen when an exception leaves a constructor.
paercebal
Matt, i can agree with you partially. The other part of me is glad that this question has been asked and now i know the answer. I learned something new from it, not that i need it but it may come in handy, so i'm glad these kinds of questions are asked ... HERE! :)
steffenj
Of course, practically this entire site is premised on the idea that people would rather ask a question, then search on MSDN... where half the answers can be found. I think the better attitude is to be helpful to someone who is asking an honest answer, or not respond. That's the point of SO. -1
Nick
Yeah its true that you can find out easily but this is one question I saw and wondered what the answer was. Now someone has asked and now someone has answered I know. The power of SO is that someone will ask something I may be interested in knowing but I've never thought of asking.
John Nolan
However, for this question, at least for C++, there are a number of subtle things that might not be displayed or answered with a simple test. Does the memory for the object get released? (yes) What about member objects - do they get destroyed? (mostly, yes) Do member pointers get deleted? (No). etc.
Michael Burr
I thought the idea was to ask the questions here and build a valuable resource rather than going elsewhere. Also this question is valid for newer developers. I think this question has merit.
Gary Willoughby
From an academic standpoint, yes - there are subtleties worth exploring in ANY topic. From a practical standpoint - chances are, a given developer works with a specific compiler, and they should treat it like their best friend - ask it questions, go exploring together, find its quirks and enjoy em!
Matt Cruikshank
In general, I agree. But I still think this is an area of C++ that is complex enough that experimentation is not necessarily the best teacher (don't know whether this is the case of C# or not).
Michael Burr
I disagree, at least in this case. In general experimentation is good, but it can be easy to transition from learning by experiment to developing by coincidence. You may get test results which are non-standard behavior specific to your compiler or your platform.
Wedge
Hmm, well, I believe that you could say this about every question on SO; if people went elsewhere instead of posting questions on SO, there would be no SO. Granted, it would take literally 5 mins to check it out yourself, but apparently this is quite an interesting topic... or its total bike-shed!
nbolton
+30  A: 

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"

Jon Skeet
This may be true for C#, but not C++.
Matt Dillard
True. Edited appropriately.
Jon Skeet
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.
Logan Capaldo
@Logan: It depends on which version of the C# spec you read as to whether they call it "finalizer" or "destructor".
Jon Skeet
+1  A: 

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#.

Greg Rogers
A: 

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"

Kapil Kapre
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.
Qwertie
+2  A: 

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.

Matt Dillard
But it should be noted that a raw pointer member which may have been initialized using new will *not* be automatically deleted.
Michael Burr
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.
MSalters
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.
Matt Dillard
A: 

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

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

Robert
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?"
paercebal
I know, designing a class with "Initialize()" beats the purpose of having a class at all.
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.
Herms
Google's code guidelines discourage using exceptions at all, so they discourage throwing from the constructor ( http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions ).
Max Lybbert
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 ( http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-c-c-and-java/ ).
Max Lybbert
+29  A: 

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.

paercebal
Another scenario, should m_pGizmo throw at new, m_pThing still leaks right? Thanks..
krebstar
No, because m_pThing is a smart pointer. If m_pThing did construct correctly, its destructor will be called if m_pGizmo throws.
paercebal
Herb Sutter's article is wrong for C# and Java. The finalizers appear to be run even if the constructors throw an exception.
David Leonard
@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: http://www.codeproject.com/KB/cs/tinguusingstatement.aspx and http://msdn.microsoft.com/en-us/library/aa664736.aspx
paercebal
@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.
paercebal
A: 

Be sure to read this before dabbling with exceptions in C++ constructors.

finnw
C++ "fanboys" should not follow this link :-)
finnw
+7  A: 

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++

MarkR
"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.
MSalters
A: 

For C++ this is addressed in a previous question: http://stackoverflow.com/questions/147572/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.

Michael Burr