Why should virtual functions not be used excessively?

前端 未结 10 2071
再見小時候
再見小時候 2021-02-19 13:16

I just read that we should not use virtual function excessively. People felt that less virtual functions tends to have fewer bugs and reduces maintenance.

What kind of b

相关标签:
10条回答
  • 2021-02-19 13:32

    In Java, there is no virtual keyword, but all methods (functions) are virtual, except the ones marked as final, static methods and private instance methods. Using virtual functions is not a bad practice at all, but because generally they cannot be resolved in compile-time, and compiler can't perform optimizations on them, they tend to be a little slower. The JVM has to figure out at run-time, which is the exact method that needs to be called. Note that this is not a big problem by any means, and you should consider it only if your goal is to create a very high-performance application.

    For example, one of the biggest optimizations in Apache Spark 2 (which runs on JVM) was to reduce number of virtual function dispatches, to gain a better performance.

    0 讨论(0)
  • 2021-02-19 13:36

    I suspect you misunderstood the statement.

    Excessively is a very subjective term, I think that in this case it meant "when you don't need it", not that you should avoid it when it can be useful.

    In my experience, some students, when they learn about virtual functions and get burned the first time by forgetting to make a function virtual, think that it is prudent to simply make every function virtual.

    Since virtual functions do incur a cost on every method invocation (which in C++ cannot usually be avoided because of separate compilation), you are essentially paying now for every method call and also preventing inlining. Many instructors discourage students from doing this, though the term "excessive" is a very poor choice.

    In Java, a "virtual" behavior (dynamic dispatching) is the default. However, The JVM can optimize things on the fly, and could theoretically eliminate some of the virtual calls when the target identity is clear. In additional, final methods or methods in final classes can often be resolved to a single target as well at compile time.

    0 讨论(0)
  • 2021-02-19 13:38

    I worked sporadically as a consultant on the same C++ system over a period of about 7 years, checking on the work of about 4-5 programmers. Every time I went back the system had gotten worse and worse. At some point somebody decided to remove all the virtual functions and replace them with a very obtuse factory/RTTI-based system that essentially did everything the virtual functions were already doing but worse, more expensively, thousands of lines more code, lots of work, lots of testing, ... Completely and utterly pointless, and clearly fear-of-the-unknown-driven.

    They had also hand-written dozens of copy constructors, with errors, when the compiler would have produced them automatically, error-free, with about three exceptions where a hand-written version was required.

    Moral: don't fight the language. It gives you things: use them.

    0 讨论(0)
  • 2021-02-19 13:39

    Virtual functions are slightly slower than regular functions. But that difference is so small as to not make a difference in all but the most extreme circumstances.

    I think the best reason to eschew virtual functions is to protect against interface misuse.

    It's a good idea to write classes to be open for extension, but there's such a thing as too open. By carefully planning which functions are virtual, you can control (and protect) how a class can be extended.

    The bugs and maintenance problems appear when a class is extended such that it breaks the contract of the base class. Here's an example:

    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();
        }
    }
    

    Here, DoubleWidget broke the parent class because Widget._thing is null. There's a fairly standard way to fix this:

    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();
        }
    }
    

    Now Widget won't run into a NullReferenceException later.

    0 讨论(0)
提交回复
热议问题