【问题标题】:Suppress delete-non-virtual-dtor warning when using a protected non-virtual destructor使用受保护的非虚拟析构函数时抑制 delete-non-virtual-dtor 警告
【发布时间】:2017-09-03 02:47:00
【问题描述】:

我有一个纯抽象接口类,以及一个实现该接口的派生类。

struct Foo
{
    virtual void doStuff() = 0;
};

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

我的接口类没有虚拟析构函数

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

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

幸运的是,我的编译器足够聪明(使用-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;
        ^

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

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

不幸的是,它还不够聪明,无法发现这个程序格式正确,并吐出类似的错误

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;
     ^

有趣的是它说可能会导致未定义的行为,而不是

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

one of Herb Sutter's Guru of the Week's 中,他给出了以下建议:

准则 #4:基类析构函数应该是公共的和虚拟的,或者是受保护的和非虚拟的。

所以让我的析构函数保护为非虚拟的。

struct Foo
{
    virtual void doStuff() = 0;
protected:
    ~Foo() = default;
};

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

现在,当我不小心尝试使用基类指针删除时,我又遇到了失败

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;
         ^

太好了,这给了我想要的东西。让我们修复代码,这样我就不会使用基类指针删除

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

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

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;
         ^

问题:

我怎样才能两全其美?

  • 当我忘记创建受保护的非虚拟析构函数并尝试通过基类指针删除时保留delete-non-virtual-dtor 错误
  • 当我使用受保护的非虚拟析构函数并通过派生类指针删除时抑制警告

超级棒的额外奖励:

  • 当我忘记使用受保护的非虚拟析构函数时抑制警告,但我通过派生类指针正确删除

【问题讨论】:

  • 你能把Bar标记为final class吗?
  • 警告似乎与编译器有关。您可能希望将编译器标记添加到帖子中。
  • @Jarod42 我可以为我的层次结构中的一些课程,但不幸的是不是所有的
  • 顺便说一句,为什么不使用虚拟析构函数?
  • 我在使用std::unique_ptr/std::shared_ptr时也没有警告。 Demo,但 unique_ptr 可能是错误的 :-(.

标签: c++ g++


【解决方案1】:

编译器告诉您问题出在 Bar 而不是 Foo。如果您要拥有另一个继承自 Bar 的类,请说 Baz:

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

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

int main()
{
    Bar* bar_ptr = new Baz();
    bar_ptr->do_stuff();
    delete bar_ptr; // uh-oh! this is bad!
}

因为 Bar 中的析构函数不是虚拟的。因此解决方案是按照建议将 Bar 标记为 final,或者将 Bar 中的析构函数设为虚拟(因为它是公共的)或按照 Herb 的建议对其进行保护。

【讨论】:

  • 也许评论一下如果我不能使它成为final,这显然意味着我将保留用户子类化它的选项,因此 要求析构函数是虚拟的。无论如何,谢谢 - 这说明了我还没有解决的问题的缺失部分
【解决方案2】:

标记类final删除警告。

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

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

Demo

【讨论】:

  • 不幸的是,这不是我用例的通用解决方案
猜你喜欢
  • 2016-01-22
  • 2020-03-06
  • 2013-11-03
  • 2012-11-23
  • 2012-11-06
  • 2023-03-23
  • 2012-02-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多