This writing was previously published in my blog, Just Like a Magic.
Overview
First, this writing concentrates of and compares between three programming languages, C#, C++/CLI, and ISO/ANSI C++. It discusses 9 rules that every developer should keep in mind while working with constructors, destructors, and finalizers and class hierarchies:
- Rule #1: Contrsuctors are called in descending order
- Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing
- Rule #3: Destructors are called in ascending order
- Rule #4: Finalizers are a feature of GC-managed objects only
- Rule #5: You cannot determine when finalizers would be called
- Rule #6: MC++ differs between destructors and finalizers
- Rule #7: In MC++ and classic C++, you can determine when destructors are called
- Rule #8: In MC++, destructors and finalizers are not called together
- Rule #9: Beware of virtual functions in constructors
Rule #1: Constructors are called in descending order
Rule #1: Constructors are called in descending order; starting from the root class and stepping down through the tree to reach the last leaf object that you need to instantiate. Applies to C#, MC++, and ANSI C++.
Let's consider a simple class hierarchy like this:
class BaseClass { public BaseClass() { Console.WriteLine("ctor of BaseClass"); } } class DerivedClass : BaseClass { public DerivedClass() { Console.WriteLine("ctor of DerivedClass"); } } class ChildClass : DerivedClass { public ChildClass() { Console.WriteLine("ctor of ChildClass"); } }
ChildClass inherits from DerivedClass, and DerivedClass, in turn, inherits from BaseClass.
When we create a new instance of ChildClass using a simple line like this:
static void Main()
{
ChildClass cls = new
ChildClass();
}
The code outputs the following results:
ctor of BaseClass ctor of DerivedClass ctor of ChildClass |
Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing
Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing; the function called before the object is fully-removed from the memory (i.e. GC-collected). Applies to C# only.
Let's consider the same class hierarchy but with destructors:
class BaseClass { public ~BaseClass() { Console.WriteLine("dtor of BaseClass"); } } class DerivedClass : BaseClass { public ~DerivedClass() { Console.WriteLine("dtor of DerivedClass"); } } class ChildClass : DerivedClass { public ~ChildClass() { Console.WriteLine("dtor of ChildClass"); } }
When you define a class destructor with that C++-alike syntax (preceding the function name with a ~) the compiler actually replaces your destructor with an override of the virtual Object.Finalize() function. That is, before the object is removed (i.e. GC-collected) from the memory, the finalizer (i.e. destructor) is called first. This finalizer first executes your code. After that it calls the finalizer of the base type of your object. If we could decompile our assembly, we would see that our destructor in the ChildClass (so other classes) has been replaced with this function:
protected virtual void Finalize()
{
try
{
Console.WriteLine("dtor of ChildClass");
}
finally
{
base.Finalize();
}
}
Rule #3: Destructors are called in ascending order
Rule #3: Destructors are called in ascending order, starting from the leaf object that you need to instantiate and moving up through the tree to reach the very first base class of your object. In reverse of constructors calling order. Applies to C#, MC++, and ANSI C++.
Now, instantiate your class:
static void Main()
{
ChildClass cls = new
ChildClass();
// 'cls' is
removed from memory here
}
the code should outputs the following results:
dtor of ChildClass dtor of DerivedClass dtor of BaseClass |
Rule #4: Finalizers are a feature of GC-managed objects
Rule #4: Finalizers are a feature of GC managed objects (i.e. managed classes). That's because the finalizer is called only when the GC removes the object from the memory (i.e. frees memory associated with).
Now, try to create a simple structure with a destructor:
struct MyStruct
{
~MyStruct()
{
Console.WriteLine("dtor of MyStruct");
}
}
The code won't compile. That's because that GC doesn't handle structures.
Rule #5: You can't determine when finalizers would be called
That's because you don't know when the next garbage collection would occur, even if you performed a manual garbage collection (using System.GC.Collect() function) you won't know exactly when memory would be released. In addition, GC always delay releasing of finalizable object, it puts them in a special GC queue called freachable (pronounced ef-reachable, F stands for Finalize) queue. Applies to C# and MC++ (.NET.)
Rule #6: MC++ differs between destructors and finalizers
Rule #6: MC++ differs between destructors and finalizers. That is, finalizers are called by GC, and destructors are called when you manually delete the object.
Let's consider the same example but in MC++:
ref class DerivedClass : BaseClass { public: DerivedClass() { Console::WriteLine("ctor of DerivedClass"); } ~DerivedClass() { Console::WriteLine("dtor of DerivedClass"); GC::ReRegisterForFinalize(this); } }; ref class ChildClass : DerivedClass { public: ChildClass() { Console::WriteLine("ctor of ChildClass"); } ~ChildClass() { Console::WriteLine("dtor of ChildClass"); GC::ReRegisterForFinalize(this); } };
When we run the code:
int main()
{
ChildClass^ cls = gcnew ChildClass();
}
it outputs the following results:
ctor of BaseClass ctor of DerivedClass ctor of ChildClass |
The destructors are not called. Why? Unlike C#, in MC++ there is a big difference between destructors and finalizers. As you know, the finalizer is called when the GC removes the object from the memory. Destructors, on the other hand, are called when you destroy the object yourself (e.g. use the delete keyword.)
Now, try to change the test code to the following:
int main() { ChildClass^ cls = gcnew ChildClass(); delete cls; }
Run the code. Now, destructors are called.
Next, let's add finalizers to our objects. The code should be like the following:

hassan wasefPosted Mar 17, 2011, 2:13 AM
thanx Really Nice topic