【发布时间】: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可能是错误的 :-(.