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?
delete-non-virtual-dtor
error for when I forget to create a protected non-virtual destructor, and I try delete through a base-class pointerSuper awesome bonus extra:
The compiler is telling you that the problem is in Bar not in Foo. If you were to have another class that inherits from Bar say Baz:
struct Baz : public Bar
{
void doStuff() override { }
};
This could lead to undefined behavior such as the case
int main()
{
Bar* bar_ptr = new Baz();
bar_ptr->do_stuff();
delete bar_ptr; // uh-oh! this is bad!
}
because the destructor in Bar is not virtual. So the solution is to mark Bar as final as has been suggested, or make the destructor in Bar virtual (since it's public) or make it protected in accordance with Herb's suggestions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With