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 typeFoowhich 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 typeBarwhich 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 typeBarwhich 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
+3


source to share


2 answers


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.

+1


source


Marking the class final

removes the warning.

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

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

      



Demo

+1


source







All Articles