Suppress "non-virtual-dtor" warning when using a protected non-virtual destructor
I have a pure abstract interface class and a derived class that implements the interface.
struct Foo
{
virtual void doStuff() = 0;
};
struct Bar : Foo
{
void doStuff() override { }
};
My interface class has no virtual destructor .
Trying to destroy a derived instance using a base class pointer is obvious, so undefined behavior
int main()
{
Foo* f = new Bar;
f->doStuff();
delete f;
}
Luckily my compiler is smart 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 behavior by making sure that I am not trying to delete using the base class pointer
int main()
{
Bar* b = new Bar;
b->doStuff();
delete b;
}
Unfortunately, it's not smart enough to realize 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, this could lead to undefined behavior rather than
Protected non-virtual destructor:
On one of Herbal Sutter gurus of the week, he gives the following advice:
Guideline # 4: Base class destructor must be public or virtual, or protected and not virtual.
Thus, let my destructor be protected not virtual.
struct Foo
{
virtual void doStuff() = 0;
protected:
~Foo() = default;
};
struct Bar : Foo
{
void doStuff() override { }
};
Now when I accidentally try to delete using the base class pointer, I get another crash
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, this gives me what I was looking for. Let's fix the code so I don't delete with the base class pointer
int main()
{
Bar* b = new Bar;
b->doStuff();
delete b;
}
Unfortunately I am getting 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 error
delete-non-virtual-dtor
when I forget to create a protected non-virtual destructor and try to delete using the base class pointer - Suppress warning when I use a protected non-virtual destructor and I delete a derived class pointer
Super awesome bonus extra:
- Suppress warning when I forget to use a protected non-virtual destructor, but I am deleting the derived class pointer correctly
source to share
The compiler tells you that the problem in Bar is not Foo . If you had another class that inherits from Bar, tell Baz:
struct Baz : public Bar
{
void doStuff() override { }
};
This can 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 suggested, or make the destructor in Bar virtual (since it's open), or make it protected as suggested by Herb.
source to share