I have a pure abstract interface class, and a derived class which implements the interface.
struct Foo
{
virtual void doStuff() = 0;
};
struct Bar : Foo
{
void doStuff() override { }
};
My interface class doesn't have a virtual destructor.
Attempting to destruct a derived instance using a base class pointer is obviously therefore undefined behaviour
int main()
{
Foo* f = new Bar;
f->doStuff();
delete f;
}
Luckily my compiler is clever enough to catch this (with -Werror
)
main.cc:15:9: error: deleting object of abstract class type ‘Foo’ which has non-virtual destructor will cause undefined behaviour [-Werror=delete-non-virtual-dtor] delete f; ^
I can avoid this undefined behaviour by ensuring I don't attempt to delete using a base class pointer
int main()
{
Bar* b = new Bar;
b->doStuff();
delete b;
}
Unfortunately it's not clever enough to pick up that this program is well formed, and spits out a similar error
main.cc:15:9: error: deleting object of polymorphic class type ‘Bar’ which has non-virtual destructor might cause undefined behaviour [-Werror=delete-non-virtual-dtor] delete b; ^
Interestingly it says might cause undefined behaviour, not will
Protected non-virtual destructor:
In one of Herb Sutter's Guru of the Week's he gives the following advice:
Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual.
So lets make my destructor protected nonvirtual.
struct Foo
{
virtual void doStuff() = 0;
protected:
~Foo() = default;
};
struct Bar : Foo
{
void doStuff() override { }
};
Now when I accidentally try to delete using a base class pointer I get another failure
int main()
{
Foo* f = new Bar;
f->doStuff();
delete f;
}
main.cc:5:2: error: ‘Foo::~Foo()’ is protected ~Foo() = default; ^ main.cc:17:9: error: within this context delete f; ^
Great, that gives me what I was looking for. Let's fix the code so I don't delete using a base class pointer
int main()
{
Bar* b = new Bar;
b->doStuff();
delete b;
}
Unfortunately I get the same error as before
main.cc:17:9: error: deleting object of polymorphic class type ‘Bar’ which has non-virtual destructor might cause undefined behaviour [-Werror=delete-non-virtual-dtor] delete b; ^
Question:
How can I get the best of both worlds?
- Keep the
delete-non-virtual-dtor
error for when I forget to create a protected non-virtual destructor, and I try delete through a base-class pointer - Suppress the warning when I use a protected non-virtual destructor, and I delete through a derived-class pointer
Super awesome bonus extra:
- Suppress the warning when I forget to use a protected non-virtual destructor, but I am correctly deleting through a derived-class pointer
Bar
asfinal class
? – Jarod42std::unique_ptr
/std::shared_ptr
. Demo, but it might be wrong forunique_ptr
:-(. – Jarod42