使用受保护的非虚拟析构函数时,禁止使用delete-non-virtual-dtor警告 [英] Suppress delete-non-virtual-dtor warning when using a protected non-virtual destructor
问题描述
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屋!