Suppress delete-non-virtual-dtor warning when using a protected non-virtual destructor

﹥>﹥吖頭↗ 提交于 2019-12-12 10:57:16

问题


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

回答1:


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.




回答2:


Marking the class final remove the warning.

struct Bar final : Foo
{
    void doStuff() override { }
};

int main()
{
    Bar* f = new Bar;
    f->doStuff();
    delete f;
}

Demo



来源:https://stackoverflow.com/questions/43282826/suppress-delete-non-virtual-dtor-warning-when-using-a-protected-non-virtual-dest

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!