Constructor Exceptions in C++, C#, and Java

I just received the following question, whose answer is the same in C++, C#, and Java.

Question: In the following code, why isn’t the destructor/disposer ever called to clean up the Widget when the constructor emits an exception? You can entertain this question in your mainstream language of choice:

// C++ (an edited version of the original code
// that the reader emailed me)
class Gadget {
Widget* w;

Interestingly, we can answer this even without knowing the calling code… but there is typical calling code that is similar in all three languages that reinforces the answer.

Construction and Lifetime

The fundamental principles behind the answer is the same in C++, C#, and Java:

A constructor conceptually turns a suitably sized chunk of raw memory into an object that obeys its invariants. An object’s lifetime doesn’t begin until its constructor completes successfully. If a constructor ends by throwing an exception, that means it never finished creating the object and setting up its invariants — and at the point the exceptional constructor exits, the object not only doesn’t exist, but never existed.

A destructor/disposer conceptually turns an object back into raw memory. Therefore, just like all other nonprivate methods, destructors/disposers assume as a precondition that “this” object is actually a valid object and that its invariants hold. Hence, destructors/disposers only run on successfully constructed objects.

In particular, if Gadget’s constructor throws, it means that the Gadget object wasn’t created and never existed. So there’s nothing to destroy or dispose: The destructor/disposer not only isn’t needed to run, but it can’t run because it doesn’t have a valid object to run against.

Incidentally, it also means that the Gadget constructor isn’t exception-safe, because it and only it can clean up resources it might leak. In the C++ version, the usual simple way to write the code correctly is to change the w member’s type from Widget* to shared_ptr<Widget> or an equivalent smart pointer type that owns the object. But let’s leave that aside for now to explore the more general issue.

A Look At the Calling Code

Next, let’s see how these semantics are actually enforced, whether by language rules or by convention, on the calling side in each language. Here are the major idiomatic ways in each language to use an Gadget object in an exception-safe way:

What if an exception is thrown while using myGadget – that is, during “do something with myGadget”? In all cases, we know that myGadget’s destructor/dispose method is guaranteed to be called.

But what if an exception is thrown while constructing myGadget? Now in all cases the answer is that the destructor/dispose method is guaranteed not to be called.

Put another way, we can say for all cases: The destructor/dispose is guaranteed to be run if and only if the constructor completed successfully.

Another Look At the Destructor/Dispose Code

Finally, let’s return to each key line of code one more time:

// C++ if( w != nullptr ) delete w;

// C# if( w != null ) w.Dispose();

// Java if( w != null ) w.dispose();

The motivation for the nullness tests in the original example was to clean up partly-constructed objects. That motivation is suspect in principle — it means the constructors aren’t exception-safe because only they can clean up after themselves — and as we’ve seen it’s flawed in practice because the destructors/disposers won’t ever run on the code paths that the original motivation cared about. So we don’t need the nullness tests for that reason, although you might still have nullness tests in destructors/disposers to handle ‘optional’ parts of an object where a valid object might hold a null pointer or reference member during its lifetime.

In this particular example, we can observe that the nullness tests are actually unnecessary, because w will always be non-null if the object was constructed successfully. There is no (legitimate) way that you can call the destructor/disposer (Furthermore, C++ developers will know that the test is unnecessary for a second reason: Delete is a no-op if the pointer passed to it is null, so there’s never a need to check for that special case.)

Conclusion

When it comes to object lifetimes, all OO languages are more alike than different. Object and resource lifetime matters, whether or not you have a managed language, garbage collection (finalizers are not destructors/disposers!), templates, generics, or any other fancy bell or whistle layered on top of the basic humble class.

The same basic issues arise everywhere… the code you write to deal with them is just spelled using different language features and/or idioms in the different languages, that’s all. We can have lively discussions about which language offers the easiest/best/greenest/lowest-carbon-footprint way to express how we want to deal with a particular aspect of object construction, lifetime, and teardown, but those concerns are general and fundamental no matter which favorite language you’re using to write your code this week.

Related

35 Responses

When an exception is thrown in a Delphi constructor, the destructor is automatically called and the exception rethrown.

This works especially well in Delphi because of two object construction conventions:

* The memory for an object’s fields is always zeroed out before the constructor is entered (TObject.InitInstance)
* The normal way to free an object in Delphi is to call the nonvirtual instance method TObject.Free, which will only invoke the destructor /if “Self” is not *nil*/.

Thus, it is safe to have a destructor that is a list of calls to ‘.Free’ on fields referring to owned objects, even though many of those objects might not have been created yet when and if an exception gets thrown in the constructor.

Getting the same semantic behaviour in other languages that are less like Delphi can be somewhat painful, because exceptions thrown in constructors need special handling – you can’t rely on destructor cleanup.

Thanks Barry because I was just exploring the varying same topic in Delphi.W32. Not sure if I have done something wrong. When I deliberately throw an exception in the constructor, the class’ destructor does not seem to be called. My trace never fired!

With respect to the acceptable practice of calling embedded object’s Free() in the destructor, in C++ all fully constructor embedded objects are automatically destroyed; only partially constructed aren’t.

Hence, as far as destruction is concerned, there isn’t much difference between Delphi and C++.

Thank you Herb.
Can you explain what happen if the constructor of a derived class throw an exception, after the base subobject has been constructed?
Does the Berived object never exist? (probably it doesn’t)
And what about the Base? (probably it does)

You probably forgot to include override on your destructor (which has to be called Destroy). Since it’s called by the framework then you need this for it to be called. But exceptions raised in a Delphi constructor do indeed result in the Destroy running.

The documentation say:

“If an exception is raised during execution of a constructor that was invoked on a class reference, the Destroy destructor is automatically called to destroy the unfinished object.”

You can also take look at the implementation of _ClassCreate in System.pas if you really don’t believe that Delphi behaves in this way.

Actually this behaviour is great for the majority of resources that are created. You have to be a little careful with non-native resources (e.g. system handles, windows etc.) but otherwise it means that exceptions in constructors can be dealt with easily.

Talking about differences between C++/Java/C# could you find the time to discuss the motivation for virtual function calls to behave differently in these languages?
I found C++’s way to be confusing at first glance but after a second thought it makes much more sense and I can’t really understand Java/C#s’ choice.
(http://en.wikipedia.org/wiki/Virtual_functions#Behavior_During_Construction)

In the last paragraph of the section “A Look At the Calling Code” Mr. Sutter said (at a general level): “[…] we can say for all cases: The destructor/dispose is guaranteed to be run if and only if the constructor completed successfully.”
What if the Widget is derived from Gadget?
Then in the first case from the quoted section, if myGadget is dynamically allocated by new Widget(), the ~Widget() will not be called if the ~Gadget() isn’t virtual.
In the second case, if the exception is thrown from Widget(), the destructor for Gadget is still called.

So, the successfully completeness of an object should be completed (mmm) with the storage location type (stack or heap) – considering that the storage is allocated before the object construction start – and with the proper use of the polymorphism rules.

I think it’s useful to point out that in C++, a common solution automatic destruction of members is to introduce a new base class, and then let the base manage object lifetimes. This is done in several parts of the GNU STL implementation (such as std::vector). I’m pretty sure there’s an example of this in “[More] Exceptional C++” as well.

I’ll throw away all the blah succeeded/called and leave only the output of the last two lines of main():

Foo constructors called: 210
Foo destructors called: 210

Let’s examine the code. The Bar did never exist just like poor Parrot ;-) But Foo::~Foo was called (a bit like paradox). I guess you should point this out in your Exceptional C++ books. It’s quite possible that a novice programmer will try to write something like that:

what’s with using dispose for C#? Dispose is not the destructor (C# has the same ~ syntax as C++ for destructors). The dispose method will never get called by the GC. user code has to explicitly call it like you did with the using syntax. GC calls the C# destructor (aka finalizer) when cleaning up the object which DOES get called even when the constructor throws an exception.

dispose was meant for unmanaged resources which have to be manually released as the GC does not know about them.

i thought i had a pretty good handle on finalize/dispose but i’m confused now…

If call the constructor which has an exception handling, how would I inform the caller about the exception? Should I then include try catch in caller? And what type of exceptions I should be prepared to deal with?

The intention is to create an Object which will analyze on its own whether data source exists and if it is not, fails and informs caller about this.

A colleague of mine uses C# these days and thought along the same lines you (and I) do. The problem he has is that something like this, in C#, prints out «Dead!» (same effect with IDisposable and Dispose(), BTW). Maybe I’m missing something, but my understanding was that C#’s finalizer (~X() in this case) would not be called, X’s constructor having never completed normally, but the fact is: it is called and it does print… Any thoughts?

Whoa, there. For Java it seems that finalizers do run if the constructor throws an exception.

The wording in the JLS for object creation says that, after allocation, the initialization either “completes abruptly” or “completes normally”. Then the section on finalization says:

“The completion of an object’s constructor happens-before (§17.4.5) the execution of its finalize method (in the formal sense of happens-before).”

And that’s all it says. The word “completion” is not qualified, so I could infer that finalizers always run after any completion, including the “abrupt completion” when a constructor throws an exception.

It’s a little sloppy because the section on creation makes a careful distinction between field initialization and the constructors. (Because of that I could also infer that if a field initialization fails then maybe the finalizer isn’t run?)