Reputation: 60451
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
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
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