2017-04-07 30 views
3

我有一个纯粹的抽象接口类和一个实现该接口的派生类。使用受保护的非虚拟析构函数时,禁止删除非虚拟dtor警告

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:基类的析构函数应该是公开和虚拟,或保护和非虚。

因此,让我的析构函数保护nonvirtual。

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错误时,我忘记了建立一个保护非虚析构函数,我试图通过一个基类指针
  • 当我使用保护非虚析构函数禁止警告删除和我通过一个派生类指针

超级真棒奖金额外删除:

  • 禁止的警告时,我忘了使用保护非虚析构函数,但我通过派生类指针正确删除
+0

你能把'Bar'标记为'final class'吗? – Jarod42

+0

警告似乎是与编译器相关的。您可能希望将编译器标签添加到帖子中。 –

+0

@ Jarod42我可以为我的层次中的一些类,但不幸的是不是所有的 –

回答

1

编译器告诉你,问题是出在酒吧没有。如果你有从酒吧继承另一个类巴兹说:

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中的析构函数虚拟化(因为它是公开的),或者根据Herb的建议进行保护。

+0

如果我*不能*使它成为'final',这可能意味着我打开了一个用户子类的选项,因此*要求*析构函数是虚拟的。无论如何,谢谢 - 这已经说明了我还没有加入的问题的缺失部分 –

1

标记类final删除警告。

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

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

Demo

+0

不幸的是,这不是一个通用的解决方案,我的使用案例 –