Vincent
Vincent

Reputation: 60451

C++ : CRTP destructor?

On a project, I have the following problem :

I have a very simple inheritance scheme (I need inheritance and not composition) :

class Base

-> class DerivedA

-> class DerivedB

-> class DerivedC

A, B and C derive from Base and that's all. So now I have 2 choices :

public inheritance with virtuality

private inheritance without virtuality

For some optimization reasons (I need a lot of inlining) I don't want virtuality ... and I don't want private inheritance. I think that the only option that remains is CRTP. But the base class have like 300 functions and implementing CRTP in it will be a real pain.

So I wonder if the following solution is valid : I use CRTP only in the destructor of the base class :

template<class TCRTP> class Base
{
    ~Base() {delete static_cast<TCRTP*>(this);}
}

where TCRTP will be DerivedA, B or C and I do public inheritance. Is it perfectly ok, or problematic ?

Thank you very much.

Upvotes: 0

Views: 1510

Answers (2)

Steve Jessop
Steve Jessop

Reputation: 279365

Your destructor is definitely wrong. The destructor of a class does not and must not delete the memory for the object.

What's your objection to public inheritance without virtual functions? There are (at least) a couple of ways to prevent someone accidentally deleting a derived object through a base pointer. One is to make the base destructor protected.

Another is to stuff dynamically-allocated instances of the derived class straight into a shared_ptr. This can even be a shared_ptr<Base>:

std::shared_ptr<Base> foo(new DerivedA(...));

Because shared_ptr has a template constructor that captures the type of its argument, the Base* pointer will be converted to DerivedA* in the deleter function associated with the shared_ptr, and hence deleted correctly. Nobody should ever be so daft as to try to extract a pointer out of a shared_ptr and delete it as Base*.

Of course if you have no virtual functions, then the trick is only really useful when the only difference between the derived classes is what they set up in their constructors. Otherwise you'll end up needing to downcast the Base* pointer from the shared_ptr, in which case you should have used a shared_ptr<DerivedA> to begin with.

Upvotes: 8

Ben Voigt
Ben Voigt

Reputation: 283803

I could see using code like inside the implementation of IUnknown::Release, but never in a destructor. The Base destructor only runs after the derived object has been destroyed, trying to delete the derived object at that point is undefined behavior. I'm pretty sure you'd get infinite recursion in that particular case.

Upvotes: 1

Related Questions