如果我有一个带有虚析构函数的基类。是否有派生类来声明虚拟析构函数?
class base {
public:
virtual ~base () {}
};
class derived : base {
public:
virtual ~derived () {} // 1)
~derived () {} // 2)
};
具体问题:
答案 0 :(得分:89)
final
来防止它在派生类中被覆盖,但这并不妨碍它成为虚拟类。virtual
关键字作为派生类中的虚函数。人们不应该一直在继承层次结构中找出函数是虚拟的。此外,如果您的类是可复制的或可移动的而无需声明自己的副本或移动构造函数,则声明任何类型的析构函数(即使您将其定义为default
)将强制您声明副本并移动构造函数和赋值运算符,如果你想要它们,因为编译器将不再为你提供它们。作为第3项的一个小点。在评论中已经指出,如果析构函数未声明,则编译器会生成默认值(仍然是虚拟的)。而默认的是内联函数。
内联函数可能会将更多程序暴露给程序其他部分的更改,并使共享库的二进制兼容性变得棘手。此外,增加的耦合可能导致在某些类型的变化面前进行大量重新编译。例如,如果您确定确实需要虚拟析构函数的实现,那么调用它的每一段代码都需要重新编译。然而,如果您已在类主体中声明它,然后在.cpp
文件中将其定义为空,那么您可以在不重新编译的情况下更改它。
我个人的选择仍然是在可能的情况下省略它。在我看来,它使代码变得混乱,并且编译器有时可以使用默认实现而不是空代码执行稍微更有效的操作。但是你可能会遇到一些限制,这使得这个选择很糟糕。
答案 1 :(得分:1)
虚拟成员函数将隐含地隐藏此函数虚拟的任何重载。
因此1)中的虚拟是“可选的”,虚拟的基类析构函数也使所有子析构函数都是虚拟的。
答案 2 :(得分:1)
答案 3 :(得分:0)
1 /是 2 /是的,它将由编译器生成 3 /宣布虚拟与否之间的选择应该遵循你的惯例来覆盖虚拟成员 - 恕我直言,两种方式都有好的论据,只需选择一个并遵循它。
如果可能的话我会省略它,但是有一件事可能会引发你的声明:如果你使用编译器生成的那个,它是隐式内联的。有时候你想要避免使用内联成员(例如动态库)。
答案 4 :(得分:0)
虚拟函数被隐式覆盖。当子类的方法与基类中的虚函数的方法签名匹配时,它将被覆盖。
这很容易造成混淆,并且有可能在重构期间中断,因此自C ++ 11起存在override
和final
关键字以明确标记此行为。有相应的警告禁止静默行为,例如GCC中的-Wsuggest-override
。
SO上的override
和final
关键字存在一个相关问题:Is the 'override' keyword just a check for a overridden virtual method?。
以及cpp参考https://en.cppreference.com/w/cpp/language/override中的文档
是否将override
关键字与析构函数一起使用仍存在争议。例如,请参见与此相关的SO问题中的讨论:default override of virtual destructor
问题在于,虚拟析构函数的语义与常规函数不同。析构函数是链接在一起的,因此所有基类的析构函数都是在子代1之后调用的。但是,在使用常规方法的情况下,默认情况下不会调用重写方法的基础实现。可以在需要时手动调用它们。