Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write proper destructors and finalizers?

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:

  1. The finalizer should clean up unmanaged resources (so everything gets cleaned up when the object is garbage-collected.
  2. The destructor should clean up managed resources (delete Foo or Foo.Dispose()?) and call the finalizer (according to 1)
  3. Both the destructor and the finalizer can be called multiple times (see 3 p. 26 end of 8.8.8)
  4. If the destructor is called the finalizer won't be called anymore (according to 1) (not by the CLR, that is, you can still call it yourself)
  5. The destructor will call base class destructors (see 3 p. 25)
  6. A class that has a finalizer should always have a destructor (presumably to deterministically clean up the unmanaged resources)
  7. A call to a finalizer will not call the base class finalizer (3 19.13.2 p. 131)

But there is also much confusion caused in part by the fact that

  1. finalizers are called destructors in C#
  2. the destructor internally generates Dispose and Finalize methods (not sure about Finalize) but the Finalize method is NOT the finalizer
  3. the semantics of destructors are different in C++ and the complexity of having both deterministic clean-up und garbage collection in general

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:

  1. Would it be acceptable to deal with the possibility of being called multiple times by just having a bool hasBeenCleanedUp member and making the entire code in the destructor/finalizer conditional on that?
  2. What kind of data can only be cleaned up by the destructor but must not be cleaned up in the finalizer because it may have been cleaned up by the gc?
like image 439
Sarien Avatar asked Oct 28 '13 12:10

Sarien


People also ask

How many Finalizers can a class have?

A class can only have one finalizer. Finalizers cannot be inherited or overloaded. Finalizers cannot be called. They are invoked automatically.

How is a destructor defined?

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() .

Does Dispose () work like destructor?

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.

Is finalize a destructor or method?

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.


1 Answers

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:

  1. Yes

  2. 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.

  3. They technically can but logically you should prevent it with a simple boolean guard

  4. Yes because all the cleanup should be done, so you ask the CLR not to finalize the object

  5. Yes because the base class knows which resources it has allocated

  6. Yes this is for deterministic cleanup

  7. You should ensure it's the case

And the others:

  1. Yes ~MyClass is mapped to an override of the Finalize method

  2. As said above the destructor is mapped to Dispose, but you should implement the finalizer yourself: !MyClass

  3. Summary: C++ destructors and Dispose pattern are for deterministic cleanup, C# destructors, C++/CLI finalizers are for non deterministic cleanup triggered by the GC.

like image 115
Pragmateek Avatar answered Oct 02 '22 00:10

Pragmateek