Why should virtual functions not be used excessively?

前端 未结 10 2070
再見小時候
再見小時候 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:19

    Every dependency increases complexity of the code, and makes it more difficult to maintain. When you define your function as virtual, you create dependency of your class on some other code, that might not even exist at the moment.

    For example, in C, you can easily find what foo() does - there's just one foo(). In C++ without virtual functions, it's slightly more complicated: you need to explore your class and its base classes to find which foo() we need. But at least you can do it deterministically in advance, not in runtime. With virtual functions, we can't tell which foo() is executed, since it can be defined in one the subclasses.

    (Another thing is the performance issue that you mentioned, due to v-table).

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

    We recently had a perfect example of how misuse of virtual functions introduces bugs.

    There is a shared library that features a message handler:

    class CMessageHandler {
    public:
       virtual void OnException( std::exception& e );
       ///other irrelevant stuff
    };
    

    the intent is that you can inherit from that class and use it for custom error handling:

    class YourMessageHandler : public CMessageHandler {
    public:
       virtual void OnException( std::exception& e ) { //custom reaction here }
    };
    

    The error handling mechanism uses a CMessageHandler* pointer, so it doesn't care of the actual type of the object. The function is virtual, so whenever an overloaded version exists the latter is called.

    Cool, right? Yes, it was until the developers of the shared library changed the base class:

    class CMessageHandler {
    public:
       virtual void OnException( const std::exception& e ); //<-- notice const here
       ///other irrelevant stuff
    };
    

    ... and the overloads just stopped working.

    You see what happened? After the base class was changed the overloads stopped to be the overloads from C++ point of view - they became new, other, unrelated functions.

    The base class had the default implementation not marked as pure virtual, so the derived classes were not forced to overload the default implementation. And finally the functon was only called in case of error handling which isn't used every here and there. So the bug was silently introduced and lived unnoticed for a quite long period.

    The only way to eliminate it once and for all was to do a search on all the codebase and edit all the relevant pieces of code.

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

    In C++: --

    1. Virtual functions have a slight performance penalty. Normally it is too small to make any difference but in a tight loop it might be significant.

    2. A virtual function increases the size of each object by one pointer. Again this is typically insignificant, but if you create millions of small objects it could be a factor.

    3. Classes with virtual functions are generally meant to be inherited from. The derived classes may replace some, all or none of the virtual functions. This can create additional complexity and complexity is the programmers mortal enemy. For example, a derived class may poorly implement a virtual function. This may break a part of the base class that relies on the virtual function.

    Now let me be clear: I am not saying "don't use virtual functions". They are a vital and important part of C++. Just be aware of the potential for complexity.

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

    The virtual table gets created for each class, having virtual functions or deriving from a class containing virtual functions. This consumes more than usual space.

    The compiler needs to silently insert extra code for ensuring that the late binding takes place instead of the early binding. This consumes more than usual time.

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

    You've posted some blanket statements that I would think most pragmatic programmers would shrug off as being misinformed or misinterpreted. But, there do exist anti-virtual zealots, and their code can be just as bad for performance and maintenance.

    In Java, everything is virtual by default. Saying you shouldn't use virtual functions excessively is pretty strong.

    In C++, you must declare a function virtual, but it's perfectly acceptable to use them when appropriate.

    I just read that we should not use virtual function excessively.

    It's hard to define "excessively"... certainly "use virtual functions when appropriate" is good advice.

    People felt that less virtual functions tends to have fewer bugs and reduces maintenance. I'm not able to get what kind of bugs and disadvantages can appear due to virtual functions.

    Poorly designed code is hard to maintain. Period.

    If you're a library maintainer, debugging code buried in a tall class hierarchy, it can be difficult to trace where code is actually being executed, without the benefit of a powerful IDE, it's often hard to tell just which class overrides the behavior. It can lead to a lot of jumping around between files tracing inheritance trees.

    So, there are some rules of thumb, all with exceptions:

    • Keep your hierarchies shallow. Tall trees make for confusing classes.
    • In c++, if your class has virtual functions, use a virtual destructor (if not, it's probably a bug)
    • As with any hierarchy, keep to a 'is-a' relationship between derived and base classes.
    • You have to be aware, that a virtual function may not be called at all... so don't add implicit expectations.
    • There's a hard-to-argue case to be made that virtual functions are slower. It's dynamically bound, so it's often the case. Whether it matters in most of the cases that its cited is certainly debatable. Profile and optimize instead :)
    • In C++, don't use virtual when it's not needed. There's semantic meaning involved in marking a function virtual - don't abuse it. Let the reader know that "yes, this may be overridden!".
    • Prefer pure virtual interfaces to a hierarchy that mixes implementation. It's cleaner and much easier to understand.

    The reality of the situation is that virtual functions are incredibly useful, and these shades of doubt are unlikely coming from balanced sources - virtual functions have been widely used for a very long time. More newer languages are adopting them as the default than otherwise.

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

    I dont know where you read that, but imho this is not about performance at all.

    Maybe its more about "prefer composition about inheritance" and problems which can occur if your classes/methods are not final (im talking mostly java here) but not really designed for reuse. There are many things which can go really wrong:

    • Maybe you use virtual methods in your constructor - once theyre overridden, your base class calls the overridden method, which may use ressources initialized in the subclass constructor - which runs later (NPE rises).

    • Imagine an add and an addAll method in a list class. addAll calls add many times and both are virtual. Somebody may override them to count how many items have been added at all. If you dont document that addAll calls add, the developer may (and will) override both add and addAll (and add some counter++ stuff to them). But now, if you youse addAll, each item is count twice (add and addAll) which leads to incorrect results and hard to find bugs.

    To sum this up, if you dont design your class for being extended (provide hooks, document some of the important implementation things), you shouldnt allow inheritance at all because this can lead to mean bugs. Also its easy to remove a final modifier (and maybe redesign it for reuseability) from one of your classes if needed, but its impossible to make a non-final class (where subclassing lead to errors) final because others may have subclassed it already.

    Maybe it was really about performance, then im at least off topic. But if it wasnt, there you have some good reasons not to make your classes extendable if you dont really need it.

    More information about stuff like that in Blochs Effective Java (this particular post was written a few days after I read item 16 ("prefer composition over inheritance") and 17 ("design and document for inheritance or else prohibit it") - amazing book.

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