2010-02-04 109 views
65

如果我有一个虚拟析构函数的基类。有派生类来声明虚拟析构函数吗?虚拟析构函数是否被继承?

class base { 
public: 
    virtual ~base() {} 
}; 

class derived : base { 
public: 
    virtual ~derived() {} // 1) 
    ~derived() {} // 2) 
}; 

具体问题:

  1. 为1)和2)一样吗?是2)因为它的基础而自动虚拟还是“停止”虚拟?
  2. 如果派生析构函数无关,可以省略它吗?
  3. 声明派生析构函数的最佳做法是什么?如果可能,声明它是虚拟的,非虚拟的还是忽略它?

回答

82
  1. 是的,它们是相同的。派生类不声明虚拟事物并不能阻止它虚拟化。实际上,如果在基类中是虚拟的,则无法阻止任何方法(包括析构函数)在派生类中虚拟化。在> = C++ 11中,您可以使用final来防止它在派生类中被重写,但这并不妨碍它成为虚拟的。
  2. 是的,如果派生类中的析构函数无关,可以省略它。它的虚拟与否并不重要。
  3. 如果可能,我会忽略它。为了清楚起见,我总是在派生类中使用virtual关键字作为虚函数。人们不应该一直沿着继承层次去找出函数是虚拟的。另外,如果你的类是可复制或可移动的,而不必声明你自己的副本或移动构造函数,那么声明任何类型的析构函数(即使你将其定义为default)都会强制你声明副本并移动构造函数和赋值运算符因为编译器将不再为你提供它们。

作为一个小点,它已经指出的评论,如果析构函数未申报的编译器生成一个默认的项目3(这仍然是虚拟的)。而且默认的是内联函数。

内联函数可能会将更多程序暴露给程序其他部分的更改,并使共享库的二进制兼容性变得棘手。另外,面对某些类型的变化,增加的耦合可能导致大量的重新编译。例如,如果您决定确实需要虚拟析构函数的实现,那么每一段调用它的代码都需要重新编译。而如果你已经在类体中声明了它,然后在.cpp文件中将其定义为空,那么你将很好地修改它而不用重新编译。

如果可能,我个人的选择仍然是省略它。在我看来,它混淆了代码,编译器有时可以通过一个空的实现默认实现,稍微高效一些。但是,你可能会受到限制,因此这是一个糟糕的选择。

+0

你最后一句应该是“不应该”而不是“应该”。 – 2010-02-04 09:07:16

+0

@Chris Lutz,我在这方面领先于你。现在它已被编辑为提交。 :-) – Omnifarious 2010-02-04 09:09:19

+1

我不同意'省略'部分。在头文件中声明并在源代码中定义它(空体)并不需要太多花费。如果您这样做,您可以随时回来并添加一些步骤(日志记录?),而不会强制客户重新编译。 – 2010-02-04 09:21:54

1

虚拟成员函数将隐含地重载此函数的任何重载。

因此,1)中的虚拟是“可选的”,基类析构函数是虚拟的,所有的子析构函数都是虚拟的。

1
  1. 与所有方法一样,析构函数是自动虚拟的。你不能阻止一个方法在C++中是虚拟的(如果它已经被声明为虚拟的,也就是说,在Java中没有相应的'final')
  2. 是的,它可以省略。
  3. ,如果我打算让于子类化这个类,不管它继承另一个类或没有,我也宁愿保持宣告虚拟方法,即使不需要的话,我就会宣布一个虚析构函数。如果您决定删除继承,这将保持子类正常工作。但我想这只是一个风格问题。
+0

析构函数不是自动虚拟的,也不是任何其他成员函数。 – 2010-02-04 16:51:34

+1

@Neil;当然不是,我指的是示例中的_析构函数(即基类具有虚函数的地方),而不是一般的析构函数。这对于所有方法都是如此,而不仅仅是析构函数。 – falstro 2010-02-04 17:39:46

0

1 /是 2 /是的,它会被编译器 3生成/宣称其虚拟或不应该遵守约定被覆盖的虚拟成员之间的选择 - 恕我直言,有很好的理由都路,只需选择一个并按照它。

如果可能的话我会忽略它,但有一件事情是可以煽动你把它声明:如果您使用生成的一个编译器,它是隐式内联。有时候你想避免内联成员(例如动态库)。