使用受保护的非虚拟析构函数时,禁止使用delete-non-virtual-dtor警告 [英] Suppress delete-non-virtual-dtor warning when using a protected non-virtual destructor

查看:3686
本文介绍了使用受保护的非虚拟析构函数时,禁止使用delete-non-virtual-dtor警告的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  struct Foo 
我有一个纯粹的抽象接口类和一个实现接口的派生类。 {
virtual void doStuff()= 0;
};

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

我的界面类没有虚拟析构函数



尝试使用基类指针来破坏派生实例显然是未定义的行为

  int main()
{
Foo * f = new Bar;
f-> doStuff();
删除f;
}

幸运的是,我的编译器非常聪明,可以抓住它(使用 -Werror


  main.cc:15:9:错误:删除具有
非虚拟析构函数的抽象类类型'Foo'的对象将导致未定义的行为[-Werror = delete-non-virtual-dtor]
delete f;
^


我可以通过确保避免这种未定义的行为我不会尝试使用基类指针进行删除。

  int main()
{
Bar * b =新酒吧;
b-> doStuff();
删除b;
}

不幸的是,选择这个程序的过程并不够巧妙,吐出类似的错误


  main.cc:15:9:错误:删除多态的对象具有
非虚拟析构函数的类类型'Bar'可能导致未定义的行为[-Werror = delete-non-virtual-dtor]
delete b;


有趣的是, / em>导致未定义的行为,而不是 will

受保护的非虚拟析构函数:
$ b

Herb Sutter的本周导师之一他给出了以下建议:

lockquote

准则#4:基类析构函数应该是public和virtual,或者protected和nonvirtual。

p>

因此,让我的析构函数保护非虚拟的。

  struct Foo 
{
virtual void doStuff()= 0;
保护:
〜Foo()=默认;
};

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

现在当我不小心尝试使用基类指针进行删除时, b
$ b

  int main()
{
Foo * f = new Bar;
f-> doStuff();
删除f;

$ / code>




  main.cc:5:2:error:'Foo ::〜Foo()'受到保护
〜Foo()= default;
^
main.cc:17:9:错误:在此上下文中
delete f;
^


寻找。让我们修复代码,这样我就不会使用基类指针进行删除了。

  int main()
{
Bar * b =新酒吧;
b-> doStuff();
删除b;
}

不幸的是,我得到和以前一样的错误


  main.cc:17:9:错误:删除具有
的多态类类型'Bar'虚拟析构函数可能导致未定义的行为[-Werror = delete-non-virtual-dtor]
delete b;


问题:



如何获得两全其美?




  • 保留 delete-non-virtual-dtor 当我忘记创建受保护的非虚拟析构函数时发生错误,并尝试通过基类指针进行删除

  • 当我使用受保护的非虚拟析构函数时,禁止警告,并通过派生类指针删除


    超级真棒奖金额外:


    • 当我忘记使用受保护的非虚拟析构函数时,禁止警告,但是我通过派生类指针


    解决方案

    编译器告诉你问题出在 Bar 不在 Foo 中。如果你有另一个继承Bar的类,请说Baz:

      struct Baz:public Bar 
    {
    void doStuff()override {}
    };

    这可能会导致未定义的行为,例如

      int main()
    {
    Bar * bar_ptr = new Baz();
    bar_ptr-> do_stuff();
    删除bar_ptr; //哦 - 哦!这不好!
    }

    因为 Bar 中的析构函数不是虚拟的。因此,解决方案是将Bar标记为最终的,或者将Bar中的析构函数虚拟化(因为它是公开的),或者根据Herb的建议进行保护。


    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

    解决方案

    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.

    这篇关于使用受保护的非虚拟析构函数时,禁止使用delete-non-virtual-dtor警告的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆