我有一个纯抽象接口类,以及一个实现接口的派生类.
struct Foo
{
virtual void doStuff() = 0;
};
struct Bar : Foo
{
void doStuff() override { }
};
我的接口类没有虚拟析构函数.
因此,尝试使用基类指针破坏派生实例显然是未定义的行为
int main()
{
Foo* f = new Bar;
f->doStuff();
delete f;
}
幸运的是我的编译器很聪明,可以捕获这个(使用-Werror)
06002
我可以通过确保不尝试使用基类指针删除来避免这种未定义的行为
int main()
{
Bar* b = new Bar;
b->doStuff();
delete b;
}
不幸的是,这个程序形成良好并且吐出类似的错误并不够聪明
06004
有趣的是,它可能会导致未定义的行为,而不会
受保护的非虚拟析构函数:
在one of Herb Sutter’s Guru of the Week’s,他给出了以下建议:
Guideline #4: A base class destructor should be either public and virtual, or protected and 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;
}
06007
太好了,这给了我正在寻找的东西.让我们修复代码,这样我就不会使用基类指针进行删除
int main()
{
Bar* b = new Bar;
b->doStuff();
delete b;
}
不幸的是我得到了和以前一样的错误
06009
题:
我怎样才能充分利用这两个世界?
>当我忘记创建受保护的非虚拟析构函数时,保留delete-non-virtual-dtor错误,并尝试通过基类指针删除
>当我使用受保护的非虚拟析构函数时,取消警告,并通过派生类指针删除
额外的超级奖金:
>当我忘记使用受保护的非虚拟析构函数时禁止警告,但我正在通过派生类指针正确删除
最佳答案 编译器告诉你问题出在Bar不在Foo中.如果你有另一个继承自Bar的班级说巴兹:
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标记为已建议的最终版本,或者将Bar中的析构函数设置为虚拟(因为它是公开的)或根据Herb的建议对其进行保护.