Managed C++ Destructors and Finalizers

I spend a lot of time moving between C++ and C#. Fortunately the languages are different enough that it’s not too difficult switching between using concepts such as stack allocated objects in C++ and garbage collected objects in C#. However, if I’m not concentrating I do run into trouble when coding in Managed C++ since I expect it to generally behave just like C++, just with access to .NET classes.

Managed C++ is really cool but it has a couple of gotchas. The one that has bitten me more than once is the difference between destructors and finalizers. To make sure that I don’t fall into the trap again I’m going to elaborate on what is a very long comment in one of our source code files.

In standard C++ you would write something like:

class MyClass
 {
public:
  MyClass() { m_pHeap = new BYTE[20]; }
  ~MyClass() { if ( m_pHeap ) delete [] m_pHeap; }
private:
  BYTE * m_pHeap;
 };

And be confident that all memory would be freed. However in Managed C++ this is not necessarily true. You see Managed C++ treats the destructor as if it was, what they call, a deterministic finalizer which basically means that it is converted into a Dispose method for the class. Therefore to make sure that the class is properly destructed a C# user of the class would need to code something like:

using (MyClass objClass = new MyClass() )
 {
  // use the class
 }

That’s a bit much to ask all users of the class to remember. Therefore Managed C++ introduces something called a Finalizer using the exclamation mark ‘!’ instead of the tilde ‘~’, which will be called when the Garbage Collector gets around to destroying any instances of MyClass if they weren’t already explicitly disposed. So we can instead write our class like this:

public ref class MyClass
 {
public:
  MyClass() { m_pHeap = new BYTE[20]; }
  !MyClass() { if ( m_pHeap ) delete [] m_pHeap; }
 private:
  BYTE * m_pHeap;
 };

This time if the user doesn’t Dispose of MyClass the Garbage Collector will call the finalizer when it destroys the instance. End of story? Sadly not. If MyClass was left like this it wouldn’t free the memory if the user DID dispose of the instance since the Managed C++ Dispose method actually calls SupressFinalize before calling the classes destructor (i.e. the method marked with the ‘~’). Therefore for our class to work properly we need to call the finalizer from the destructor:

public ref class MyClass
{
public:
  MyClass() { m_pHeap = new BYTE[20]; }
  !MyClass() { if ( m_pHeap ) delete [] m_pHeap; }
  ~MyClass() { this->!MyClass(); }
private:
  BYTE * m_pHeap;
};

So, do you always just free everything in the finalizer and simply call the finalizer from the destructor? If only life were so simple. You need to think about the fundamental differences between the two methods. Since the finalizer is non-deterministic, i.e. you don’t know when it will be called, you should only free objects that would otherwise never be freed, i.e. unmanaged objects. You don’t need to worry about managed objects since they will be, or may have already been, handled by the Garbage Collector. Therefore you should only free (ie dispose) managed objects in your destructor since that is the deterministic finalizer, which will only be called when your class is being specifically disposed of rather than left for the Garbage Collector to cleanup up.

Still confused? Try reading someone else’s take on it.

4 thoughts on “Managed C++ Destructors and Finalizers

  1. Jens

    How can one free managed objects? Should the following statement:

    Therefore you should only free managed objects in your destructor since that is the deterministic finalizer.

    rather read:

    Therefore you should only dispose managed objects in your destructor since that is the deterministic finalizer

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s