RnR
RnR

Reputation: 2115

How do I make destructors protected for a whole class hierarchy in a maintainable way?

I would like to make sure no one is able to delete any objects from my class hierarchy other then by using a provided Destroy method.

The rationale is that any object from this hierarchy needs to take a special write mutex before it starts to destroy itself to make sure objects are not deleted while another thread is using them.

I know I could prevent this problem with reference counting but it would be a much bigger change to the system also in terms of potential performance impact and memory allocation.

Is there a way to somehow efficiently/smartly make all the destructors protected so that child classes can call their parents destructors while outsiders have to use Destroy?

One solution that is safe (ie. it will not rot) that I came up with is to make all the destructors private and declare each derived class as a friend of the base class but I'd prefer something more elegant, less manual and easier to maintain (like not requiring to modify base classes in order to derive from them).

Is there anything like this available? Maybe some smart trick that makes things "work" as I'd like?

ps. The solution I chose for now is to NOT prevent anyone from calling delete in all cases (just made it protected in the base class) but detect this situation and call abort in the base class destructor.

Upvotes: 5

Views: 219

Answers (4)

prapin
prapin

Reputation: 6897

I had the same needs, but for a different reason. In our company framework, nearly all classes derive from a common BaseObject class. This object uses a reference count to determine its life time. BaseObject has in particular these three methods: retain(), release() and autorelease(), heavily inspired from Objective-C language. The operator delete is only called inside release() when the retain count reaches 0. Nobody is supposed to call delete directly, and it is also undesirable to have BaseObject instances on stack.

Therefore, all our destructors should be protected or private. To enforce this, as I know it is impossible from the language, I wrote a Perl script that looks for all destructors within a source directory and makes a report. It is then relatively easy to check that the rule is respected.

I made the script public, available here: https://gist.github.com/prapin/308a7f333d6836780fd5

Upvotes: 1

RnR
RnR

Reputation: 2115

Thanks for all your feedback and discussion. Yes - it proved it's impossible to do what would be my natural first choice :( (to have the "protection" of the destructor take effect in derived classes just as it's "virtuality" does).

My solution in this particular case (solving all potential problems with being able to make hones mistakes by introducing new derived classes that violate previous agreements AND keeping the solution in one place/maintainable (no code duplication in derived classes etc)) is:

  1. Make all the existing class hierarchy destructors I can find protected
  2. Provide a Destroy method in the base class that can be used to initiate the destruction of these objects - if called the method lights up a flag on the destructed object that it was properly destroyed and then calls delete on it
  3. In the base class destructor (once we get to it) check the flag - if it's not set it means someone introduced a new class and called delete on it directly or avoided the compilers protection checks in some other way (abused some friendships etc) - I abort the application in this case to make sure the issue cannot be ignored/missed

Upvotes: 1

Jan Herrmann
Jan Herrmann

Reputation: 2767

It can be done with help of testing. For a class with an protected destructor you need 2 test cases:

  1. one function (in one file) which fails to compile simply creating such an object
  2. one function (in second file) which creates an object with an derived class which compiles.

If both test cases work I think you can be sure your classes are protected as you like.

I don't know wether you are able to implement it with your build system but I have an example using bjam (from boost) at git hub. The code is simple and works for gcc and msvc. If you don't know bjam you should look inte Jamroot.jam. I think it is clear without any further comment how this simple example works.

Upvotes: 0

Open AI - Opting Out
Open AI - Opting Out

Reputation: 24163

Don't try to reinvent the lifetime mechanisms provided by the language.

For an object of your class to be correctly initialised it needs to also be able to clean itself up.

In its constructor pass either the mutex, or a means of obtaining a mutex, which it can use in its destructor.

Upvotes: 1

Related Questions