Desctructor, finalizer, and Dispose - Part 1.managed C++ extension in V1.X

As a C++ fan, I'm a long time admirer for deterministic finalization. I think introduction of garbage collection to C style language by Java and .Net is a huge improvement. However, I found lose of deterministic destructor is almost unacceptable when I first enter Java/.Net world. Of course I'm used to it now, but it's still quite confusing to me for C# to use C++ destructor syntax for Finalizer. And in managed extension of C++, destructor becomes something totally different for managed data type. I bet a lot of experienced C++ developers make mistake to use finalizer as if it was destructor when they first try .Net. So when I read the new changes in Whidbey version of C++ from Stan Lippman's blog, I'm very excited and can't wait to give it a try.

But before we look into the new features, let's go over how old version of managed C++ handles destructors. I wrote this simple program:

#using <mscorlib.dll>

using namespace System;

__gc class RefT
{
public:
RefT () {Console::WriteLine ("RefT::RefT");}
~RefT () {Console::WriteLine ("RefT::~RefT");}
};

 

__value class ValueT
{
ValueT () {Console::WriteLine ("ValueT::ValueT");}
//destructor is not allowed for value type
};

int main()
{
{ //1. auto-generated finalizer will be called in asynchronizied fashion
RefT * prt = new RefT;
}

   { //2. Dispose is called at “delete” and finalizer will be suppressed
RefT * prt = new RefT;
delete prt;
}

  {
ValueT vt;
}

{
//value type can't be created in GC heap
ValueT * pvt = __nogc new ValueT;
delete pvt;
}
return 0;
}

I compiled it with V1.1 C++ compiler and checked generated IL code using ildasm. RefT is compiled to something like this:

class RefT
{
RefT ()
{
Console.WriteLine (“RefT::RefT”);
}

   void Finalize ()
{
Console.WriteLine (“Ref::~Ref”);
}

   void __dtor ()
{
GC.SuppressFinalize (this);
Finalize ();
}
}

Here we could see that C++ destructor is mapped to CLR finalizer and a method “__dtor” is added to call finalizer and SupressFinalize.

In main, the first block creates a RefT object in heap and leaves it as garbage. Sometime later, finalizer (~RefT) will run and the object will be collected. In the second block, we “delete” the object. This is translated into a call to __dtor in IL. So “delete” acts more like Dispose method recommended by IDisposable pattern: the object is not freed but the contents are disposed and finalizer won't run on the object later.