我应该在基类中将所有函数声明为虚函数吗?

11

当我声明一个基类时,是应该将其中所有的函数都声明为虚函数,还是应该有一组虚函数和一组非虚函数,我确信后者不会被继承?

8个回答

8

只有在派生类需要以不同的方式实现函数时,该函数才需要是虚函数。

例如:

class Base {
public:
  void setI (int i)  // No need for it to be virtual
  {
    m_i = i;
  }

  virtual ~Base () {}         // Almost always a good idea

  virtual bool isDerived1 ()  // Is overridden - so make it virtual
  {
    return false;
  }

private:
  int m_i;
};

class Derived1 : public Base {
public:
  virtual ~Derived () {}

  virtual bool isDerived1 ()  // Is overridden - so make it virtual
  {
    return true;
  }
};

作为结果,我倾向于不使用任何虚拟的东西,除非你事先知道你打算覆盖它,或者直到你发现你需要该行为。唯一的例外是析构函数,在基类中它几乎总是需要是虚拟的。

2
当一个类中至少有一个方法是虚方法时,你应该拥有虚析构函数。 - stefanB

4

只有你打算和设计要被覆盖的函数才应该被声明为虚函数。将方法声明为虚函数在维护和性能方面都不是免费的(在我看来,维护成本更高)。

一旦一个方法被声明为虚函数,就会更难理解使用该方法的任何代码。因为你必须考虑在这种情况下N个方法调用会做什么,而不是考虑一个方法调用会做什么。N代表覆盖该方法的子类数量。

唯一的例外是析构函数。在任何想要派生的类中,析构函数都应该是虚函数。这是确保在释放时调用正确析构函数的唯一方法。


4
非虚拟接口模式(C++编码标准第39项)指出,基类应该有非虚拟接口方法,以允许基类保证不变量,并具有非公共的虚拟方法,以便派生类可以自定义基类行为。非虚拟接口方法调用虚拟方法来提供可重载的行为。

考虑在此处添加源。发现您参考了“C++编码标准,101条规则、指南和最佳实践,Herb Sutter和Andrej Alexandrescu”。然而,在C++中没有官方的编码标准。 - patrik

2

我倾向于只将我想要重写的内容设置为可重写的虚函数。如果我的初始假设关于我想要重写的内容是错误的,我会回去更改基类。

哦,如果你正在处理将被继承的东西,显然始终要使你的析构函数成为虚函数。


1

如果您正在创建一个基类(您确定有人派生该类),则可以执行以下操作:

  • 使析构函数虚拟(基类必须)
  • 定义应派生的方法并将它们设置为虚拟。
  • 定义不需要(或不应)派生的方法作为非虚拟方法。
  • 如果这些函数仅适用于派生类而不适用于基类,则将其标记为受保护的。

1
编译器不知道当基类型指针调用虚函数时将运行哪个实际代码。因此,根据基类指针所指向的对象,在运行时需要评估将要运行的实际代码。如果函数在继承类中不会被覆盖,请避免使用虚函数。
简短版本: “您应该拥有一组虚函数和一组非虚函数,您确定这些函数不会被继承。”因为虚函数会导致运行时性能下降。

0

接口函数通常应该是虚函数。提供固定功能的函数则不应该是虚函数。


0
为什么在你真正重写它之前要声明某些东西是虚拟的?我认为这不是确定或不确定的问题。遵循事实:它是否被覆盖?没有?那么它就不应该是虚拟的。

这导致一个情况,即如果您想更改一个子类(以覆盖先前未覆盖的方法),则必须更改基类(使方法虚拟),这意味着您必须重新编译所有引用基类的代码。在某些项目中,基类的脆弱性不是问题,但在其他项目中则可能会出现问题。这取决于您的构建过程以及是否已发布了基类的接口。 - Steve Jessop
1
所以,在我的看法中,只有在允许子类重写它们(并支持运行时多态性)的情况下,方法才应该是虚拟的,而不是实际上是否这样做。 - Steve Jessop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接