在我的设计中,有时我会遇到添加/删除虚拟方法的情况。我知道的经验法则是,一旦我有虚拟方法,我将拥有一个虚拟析构函数。
我的问题:如果我在创建类时立即添加虚拟析构函数会有什么危害(即使还没有虚拟方法)?基本上这个想法不会在以后忘记它。特别是对于n个派生类,我不需要在以后的n个地方更改它。
答案 0 :(得分:5)
虚拟功能表的大小有很小的开销。可能不值得担心。虚拟析构函数还将使您的类成为非聚合类,非平凡类,非标准布局类,因此也是非POD类。根据手头的问题,这可能是不合需要的。
但是,我建议您专门设计您的类是多态还是非多态。如果它们将以多态方式使用,请为它们提供虚拟析构函数。如果没有,不要。如果您需要更改它,请在需要时进行更改。
答案 1 :(得分:1)
唯一的好处是你的类和所有派生类都有一个v-table,它的大小略有增加。 即使您决定稍后将基类析构函数设置为虚拟,您也不需要在派生类中进行任何更改。对于包括析构函数在内的任何方法,您只需要在基类中使用一次虚拟关键字。派生类中的相同方法自动变为虚拟。
作为替代方案,您可以使您的析构函数受到保护。这将防止使用基类指针意外调用删除。
class A
{
protected:
~A(){}
};
class B : public A
{};
int main(int argc, char *argv[])
{
A * p = new B;
delete p;
}
在我的编译器上,它会出现以下错误
错误C2248:'A ::〜A':无法访问类'A'中声明的受保护成员 a.cpp(9):编译器在这里生成了'A :: ~A' a.cpp(6):见“A”的声明
答案 2 :(得分:1)
不,即使您没有任何其他虚拟方法,拥有虚拟析构函数也非常有意义。
但是,如果内存使用很重要,则每个字节都会计数,如果您没有任何虚拟方法,则可以获得4或8个字节。在我的应用程序中,我有一些类,其中有数百万个实例。在这种情况下,在你的班级中摆脱一个v-pointer真的很有意义。
如果您将析构函数从非虚拟更改为虚拟(反之亦然),我并不完全理解为什么需要更改派生类。一旦方法是虚拟的,即使您没有指定虚拟方法,所有派生类中的相同方法也将是虚拟的。然而,由于样式原因,可能建议在派生类中添加虚拟,即使不需要它。
答案 3 :(得分:1)
不要盲目遵守规则。也就是说,遵循规则,但不要盲目地做。
真正需要虚拟析构函数的唯一情况是通过其基础对象指针删除对象。经验法则概括并简化了这种情况:如果一个对象可能通过其基础对象指针被删除,那么它将被多态地使用;多态对象可能具有虚函数,并且具有虚函数的对象可能被多态地使用;因此,具有虚函数的对象可能需要虚拟析构函数。
这一切都很好,花花公子,规则大部分都有效,但有一个更重要,更基本的事实很少被提及,部分原因是这些规则确实有效。事实上,存在类似于价值的对象,并且存在另一种类型的对象,这些对象没有好名字,但我将其称为类似实体的对象。类似于实体的对象具有与它们的值不同的标识,它们使用引用语义,它们不应该在没有充分理由的情况下被复制(例如创建单独的标识),它们可能被多态地访问等等。类似于对象的对象没有标识除了它们的价值,它们可以自由复制,不应该多态使用等等。它们是如此不同,值得为它们的类提供不同的关键字!在设计课程时,您必须决定它属于哪个类别。然后你的问题就解决了。实体获得虚拟析构函数,值不会。
答案 4 :(得分:0)
拥有虚拟析构函数的唯一原因是可以通过指向基类型的指针删除派生类型的对象。如果这是类的设计所要求的,那么必须具有虚拟析构函数,即使它没有任何其他虚函数。如果设计不包括通过指向base的指针删除,则它不需要虚拟析构函数,即使它具有虚函数。腰带和吊带的人会告诉你无论如何都要使破坏者变得虚拟,因为它不会伤害任何东西,而且,你永远不会知道。这不是技术原因;这是一个策略选择,以防止不阅读和遵循文档的用户。