为什么虚拟功能不能过度使用?

GG.*_*GG. 12 c++ java virtual-functions

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

由于虚函数会出现什么样的错误和缺点?

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


我能想到的一个原因是由于v表查找,虚函数可能比正常函数慢.

Ste*_*hen 14

你发布了一些一揽子声明,我认为大多数务实的程序员会因为误导或误解而耸耸肩.但是,确实存在反虚拟狂热者,他们的代码对性能和维护也同样糟糕.

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

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

我刚才读到我们不应该过度使用虚函数.

很难定义"过度"......当然"在适当的时候使用虚拟功能"是一个很好的建议.

人们认为虚拟功能越少,错误越少,维护也越少.由于虚函数,我无法获得可能出现的错误和缺点.

设计糟糕的代码很难维护.期.

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

所以,有一些经验法则,都有例外:

  • 保持层次结构浅薄.高大的树木令人困惑.
  • 在c ++中,如果你的类有虚函数,使用虚拟析构函数(如果没有,它可能是一个bug)
  • 与任何层次结构一样,保持派生类和基类之间的"is-a"关系.
  • 你必须要知道,根本不能调用虚函数...所以不要添加隐含的期望.
  • 有一个难以争辩的案例,即虚拟功能较慢.它是动态绑定的,所以情况经常如此.在大多数案例中是否重要,其引用肯定是有争议的.简介和优化:)
  • 在C++中,不要在不需要时使用虚拟.标记虚函数涉及语义 - 不要滥用它.让读者知道"是的,这可能会被覆盖!".
  • 首选纯虚拟接口到混合实现的层次结构.它更清晰,更容易理解.

这种情况的实际情况是虚拟功能非常有用,并且这些疑问不太可能来自平衡源 - 虚拟功能已经被广泛使用了很长时间.更新的语言正在采用它们作为默认语言.


Mat*_*ell 7

虚函数比常规函数稍慢.但这种差异是如此之小,以至于除了最极端的情况之外,并没有产生任何影响.

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

编写可以扩展的类是一个好主意,但是有太多开放的东西.通过仔细规划哪些功能是虚拟的,您可以控制(并保护)如何扩展类.

当类被扩展以破坏基类的契约时,会出现错误和维护问题.这是一个例子:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这里,DoubleWidget打破了父类,因为它Widget._thing是null.有一个相当标准的方法来解决这个问题:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在Widget不会再遇到NullReferenceException.

  • "虚函数比常规函数略慢." 不,他们不是,因为他们做得更多.在非虚函数周围添加必要的if/else链或switch语句以便进行一些动态调度之后,非虚函数的性能可能会更差. (2认同)

Igo*_*kon 6

每个依赖项都会增加代码的复杂性,并使维护更加困难.当您将函数定义为虚拟时,可以创建类对其他代码的依赖关系,这些代码目前可能甚至不存在.

例如,在C中,您可以轻松找到foo()的作用 - 只有一个foo().在没有虚函数的C++中,它稍微复杂一些:你需要探索你的类及其基类来找到我们需要的foo().但至少你可以提前确定性地做,而不是在运行时.使用虚函数,我们无法分辨执行哪个foo(),因为它可以在子类中定义.

(另一件事是你提到的性能问题,由于v-table).