I am trying to figure out how to properly clean up after my objects in C++/CLI.
I have read or skimmed these two articles (one, two) and looked at the standard and looked at some other questions notably this one.
I have various pieces of information:
But there is also much confusion caused in part by the fact that
What I would like as an answer is an example of a class with all the different kinds of data that it might contain (managed, unmanaged, managed but disposable, whatever else you can think of) and properly written destructor and finalizer.
I have two more specific question:
bool hasBeenCleanedUp
member and making the entire code in the destructor/finalizer conditional on that?A class can only have one finalizer. Finalizers cannot be inherited or overloaded. Finalizers cannot be called. They are invoked automatically.
A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete . A destructor has the same name as the class, preceded by a tilde ( ~ ). For example, the destructor for class String is declared: ~String() .
Destructor implicitly calls the Finalize method, they are technically the same. Dispose is available with objects that implement the IDisposable interface. The destructor implicitly calls Finalize on the base class of the object.
Finalizers (historically referred to as destructors) are used to perform any necessary final clean-up when a class instance is being collected by the garbage collector. In most cases, you can avoid writing a finalizer by using the System. Runtime.
Not a complete answer to your question but too long to fit a comment.
In a fully managed world, where each object only references managed objects, there is no need for finalizers or destructors, because the only resource is memory and the GC takes care of it.
When you reference unmanaged resources you have the responsibility of releasing them when you don't need them anymore.
So you need to implement a dedicated cleanup code.
There is 2 possibilities:
you know when you don't need the unmanaged resources anymore so you can deterministically run your cleanup code, this is implemented through destructors/Dispose
you don't know when these resources won't be needed anymore so you postpone the cleanup at the last possible moment, when the object that wraps the resources is collected by the GC, this is implemented through finalizers
You guess it's far better to be in the first situation because you don't consume more memory than you need and you avoid some additional overhead to the GC process.
You typically implement both because the life-time of the instances can vary from usage to usage.
At the CLR level there is no such things as deterministic cleanup, only finalizers.
At the language/API level there is support for deterministic cleanup:
in native C++ you have destructors called either when exiting a scope or when "deleting"
in the .Net world you have the Dispose pattern
in the pure managed C++/CLI world destructors are mapped to Dispose
When you have the chance to know exactly when you can run the cleanup code you call (or let the infrastructure call) the destructor. Once the cleanup is finished you can get rid of all the finalization process, so that the object can be collected immediately at the next GC.
Some clarifications about your first series of points:
Yes
The destructor is responsible for the cleanup of unmanaged resources too; it can call the finalizer if it's where you've factorized the cleanup code.
They technically can but logically you should prevent it with a simple boolean guard
Yes because all the cleanup should be done, so you ask the CLR not to finalize the object
Yes because the base class knows which resources it has allocated
Yes this is for deterministic cleanup
You should ensure it's the case
And the others:
Yes ~MyClass is mapped to an override of the Finalize method
As said above the destructor is mapped to Dispose, but you should implement the finalizer yourself: !MyClass
Summary: C++ destructors and Dispose pattern are for deterministic cleanup, C# destructors, C++/CLI finalizers are for non deterministic cleanup triggered by the GC.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With