2013-03-24 119 views
1

在我的设计过程中,有时会遇到添加/删除虚拟方法的情况。我知道的经验法则是,一旦我拥有虚拟方法,我将拥有一个虚拟析构函数。没有虚拟方法的虚拟析构函数有什么危害吗?

我的问题:如果在创建类时立即添加虚拟析构函数(即使没有虚拟方法),是否有任何伤害?基本上这个想法以后不要忘记。特别是对于n个派生类,我不需要在以后的n个地方改变它。

回答

5

有一个在虚函数表的规模很小开销的声明。可能不值得担心。一个虚拟析构函数也会使你的类成为一个非集合类,一个非平凡类,一个非标准布局类,因此也是一个非POD类。这可能是不受欢迎的,这取决于手头的问题。

但是,我建议特别设计你的类是多态的或不是。如果他们将要多态使用,给他们一个虚拟的析构函数。如果没有,不要。如果您需要更改它,请在需要时进行更改。

+3

我很惊讶大家在这里谈论虚拟表的开销,这是边缘的,但​​不是'虚拟'析构函数的存在使得该类可能不是期望的非POD类的事实。提醒我一个[类似的问题](http://stackoverflow.com/a/8298219/452307)我前段时间回答 – 2013-03-24 11:45:35

+0

@AlokSave被盗! – 2013-03-24 11:48:36

+0

@Alok保存它也使它非聚合,这可能是一个问题。 – juanchopanza 2013-03-24 12:13:24

1

唯一的危害是你的类和所有派生类将有一个v-表,这是一个边际增加的大小。 即使您决定稍后创建基类析构函数,您也不需要在派生类中进行任何更改。对于任何包含析构函数的方法,您只需要在基类中使用虚拟关键字一次。派生类中的相同方法自动变为虚拟。

作为替代方案,您可以使您的析构函数受到保护。这将防止使用基类指针的意外调用。

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'

1

不,即使您没有任何其他虚拟方法,拥有虚拟析构函数也是非常有意义的。

但是,如果内存使用率很重要,则每个字节都会计数,如果您没有任何虚拟方法,则可以获得4或8个字节。在我的应用程序中,我有一些类,其中有数百万个实例。在那种情况下,在你的班级中摆脱一个v-pointer真的很有意义。

如果将析构函数从非虚拟变为虚拟(反之亦然),我并不完全遵循为什么需要更改派生类。一旦一个方法是虚拟的,即使你没有指定虚拟,同样的方法在所有派生类中都是虚拟的。尽管如此,由于风格的原因,可能会建议在派生类中添加虚拟,即使不需要。

1

不要盲目遵守规则。也就是说,遵守规则,但不要盲目做。

真正需要虚拟析构函数的唯一情况是何时通过其基础对象指针删除对象。经验法则概括并简化了这种情况:如果一个对象可能通过其基础对象指针被删除,那么它将被多形地使用;多态对象可能具有虚拟功能,并且具有虚拟功能的对象可能被多态使用;因此,具有虚函数的对象可能需要虚拟析构函数。

这一切都很好,很规则,大多数都适用,但有一些更重要和更基本的事实很少提及,部分原因是这些规则确实有效。事实上,有价值的对象,还有另一种对象,它没有一个好名字,但我会称它们为实体对象。像实体一样的对象具有与它们的值分离的身份,它们使用引用语义,不​​应该在没有充分理由(例如创建单独的身份)的情况下被复制,它们可能被多态地访问等等。类似值的对象没有身份除了它们的价值之外,它们可以被自由地复制,它们不应该被多形地使用,等等。它们是如此不同以至于它们的类有不同的关键字是值得的!当你设计你的课程时,你必须决定它属于哪个类别。那么你的问题就解决了。实体获得虚拟析构函数,值不。

0

拥有虚拟析构函数的唯一原因是可以通过指向基类型的指针删除派生类型的对象。如果这是该课程的设计要求,那么它必须具有虚拟析构函数,即使它没有任何其他虚拟函数。如果设计不包括通过指向base的指针删除,则不需要虚拟析构函数,即使它具有虚函数。腰带和吊带人会告诉你无论如何都要使析构函数变成虚拟的,因为它不会伤害任何东西,而且,你永远不会知道。这不是技术原因;这是一种政策选择,可防止不阅读和遵循文档的用户。

相关问题