为什么虚函数不能过度使用?

2022-09-04 06:05:10

我刚刚读到,我们不应该过度使用虚函数。人们认为,虚拟功能越少,错误就越少,维护也就越少。

由于虚拟功能,会出现什么样的错误和缺点?

我对C++或Java的上下文感兴趣。


我能想到的一个原因是,由于v表查找,虚拟函数可能比普通函数慢。


答案 1

你已经发布了一些笼统的声明,我认为大多数务实的程序员会耸耸肩,因为他们被误导或误解了。但是,确实存在反虚拟狂热分子,他们的代码对性能和维护来说可能同样糟糕。

在Java中,默认情况下一切都是虚拟的。说你不应该过度使用虚函数是相当强大的。

在C++中,您必须声明一个函数 virtual,但是在适当的时候使用它们是完全可以接受的。

我刚刚读到,我们不应该过度使用虚函数。

很难定义“过度”...当然,“在适当的时候使用虚函数”是很好的建议。

人们认为,虚拟功能越少,错误就越少,维护也就越少。我无法得到由于虚拟功能而会出现什么样的错误和缺点。

设计不佳的代码很难维护。时期。

如果你是一个库维护者,调试隐藏在高类层次结构中的代码,那么很难跟踪代码实际执行的位置,如果没有强大的IDE的好处,通常很难说出哪个类覆盖了行为。它可能导致在跟踪继承树的文件之间跳来跳去。

因此,有一些经验法则,但都有例外:

  • 保持层次结构浅层。高大的树木使阶级感到困惑。
  • 在 c++ 中,如果你的类有虚函数,请使用虚拟析构函数(如果没有,它可能是一个错误)
  • 与任何层次结构一样,保持派生类和基类之间的“is-a”关系。
  • 您必须注意,虚函数可能根本不会被调用...所以不要添加隐含的期望。
  • 有一个很难说的案例是,虚函数的速度较慢。它是动态绑定的,所以经常是这样。在引用它的大多数情况下,它是否重要当然是值得商榷的。:)分析和优化
  • 在C++,不要在不需要时使用虚拟。标记函数虚拟涉及语义含义 - 不要滥用它。让读者知道“是的,这可能会被覆盖!
  • 更喜欢纯虚拟接口,而不是混合实现的层次结构。它更干净,更容易理解。

现实情况是,虚拟功能非常有用,而这些怀疑的阴影不太可能来自平衡的来源 - 虚拟功能已经被广泛使用了很长时间。更多的新语言采用它们作为默认语言。


答案 2

虚函数比常规函数稍慢。但这种差异是如此之小,以至于除了最极端的情况之外,在所有情况下都没有区别。

我认为避免虚拟功能的最佳理由是防止接口滥用。

编写开放以进行扩展的类是个好主意,但是有这样的事情太开放了。通过仔细规划哪些函数是虚拟的,您可以控制(和保护)类的扩展方式。

当一个类被扩展时,会出现 bug 和维护问题,以至于它破坏了基类的协定。下面是一个示例:

class Widget
{
    private WidgetThing _thing;

    public virtual void Initialize()
    {
        _thing = new WidgetThing();
    }
}

class DoubleWidget : Widget
{
    private WidgetThing _double;

    public override void Initialize()
    {
        // Whoops! Forgot to call base.Initalize()
        _double = new WidgetThing();
    }
}

在这里,DoubleWidget 破坏了父类,因为是 null。有一个相当标准的方法可以解决这个问题:Widget._thing

class Widget
{
    private WidgetThing _thing;

    public void Initialize()
    {
        _thing = new WidgetThing();
        OnInitialize();
    }

    protected virtual void OnInitialize() { }
}

class DoubleWidget : Widget
{
    private WidgetThing _double;

    protected override void OnInitialize()
    {
        _double = new WidgetThing();
    }
}

现在,Widget 以后不会遇到这种情况。NullReferenceException


推荐