Philosophical discussion on when to mark a method as virtual


When should you mark a method as virtual? This question has both a technical and a philosophical aspect. The technical aspect is well-understood: You mark a method as virtual if you want calls to the method to be invoked on the run-time type of the invoked object rather than on the compile-time type of the invoking reference.

But there is a heavy philosophical aspect to this question as well. Some people argue that every method should be virtual so that derived classes can override the behavior of the base class in ways the base class may not have anticipated. This grants you maximum flexibility, avoiding having to redesign the class in the future.

I happen to believe that the cost of this extensibility is badly underestimated. Once you mark a method as virtual, your life becomes much more complicated. Every virtual method is an extensibility point, which means that you have to assume the worst. You can't hold any locks when calling a virtual method (else you may deadlock with the derived class). You have to be prepared for all sorts of reentrancy because the derived class may have decided to call MessageBox in its implementation. You have to revalidate your state after the virtual method returns because the derived class may have done "crazy" things inside that virtual method including possibly destroying the object itself! (Life gets very interesting when control returns to the base class with an invalid "this" pointer.)

It takes only a few seconds to type the word "virtual" but you need to know what you just signed yourself up for. I'm not saying that virtual methods are bad; I'm saying that you need to understand what you are doing. You don't say "Gosh, somebody might want to override this, so I'll make it virtual." You have to say, "Gosh, I'm creating an extensibility point here that needs to be designed and tested."

Of course, if your class is private, then you are the only person who can derive from the class, and therefore you can impose rules on what the derived classes are permitted to do if they override a method, and you have the power to enforce said rules on those derived classes since you're the one who wrote them anyway. But if you allow others to derive from your class, then you have entered the world of extensibility and your life has just gotten a lot more complicated.

Comments (30)
  1. Dave says:

    I tend to do the extreme opposite, and write classes to explicitly disable derived classes–and even operations like assignment–unless I have thought through the implications. That allows me to be lazy and only do the design changes when I actually need the functionality and flexibility. At that point I usually have a better idea of what is really needed anyway.

    That sort of approach won’t work for writing a public class library of course. My condolences to those guys.

  2. Roland says:

    My rule of thumb is to avoid making public methods virtual. Because if I have a public virtual method and some time later I notice "oh, there’s some code that should be called shortly before or after any code in the derived class is called", I’m out of luck.

    That’s why I tend to use a pattern something like this:

    protected virtual void OnPerformAction()

    {

     // default implementation

    }

    public void PerformAction()

    {

     // code may be added later here…

     OnPerformAction();

     // … or here

    }

  3. Jack Mathews says:

    Seems like a metaphor for Win16 -> Win32 -> Win64…

  4. richard says:

    This discussion has many different points of view and ways of being argued – it all depends on the purpose of your class.

    For some objects, it does not make sense to derive from them, so virtual functions are pointless.

    For other objects, the name of the game is polymorphism and virtual functions are essential.

    I take pretty much the opposite view of Roland and make most of my public functions virtual (if it makes sense to do so).

    One thing that bugs me is function hiding when you forget to declare a function as virtual.

    I think the C++ compiler has a long way to go to keep us programmers from tripping over our code.

  5. Brian says:

    I guess I’m more concerned with the security aspects. A ‘contract’ or ‘intent of the method’ is out the window if the deriver’s intent is malicious.

  6. Brian says:

    I know this isn’t a .NET blog but that is my world at this point. With dynamic loading and factory patterns and such, I can see where someone might be able to get malicious code injected into an application.

    Not a point of contention here – just one of the things I take into account when making the virtual/non-virtual decision.

  7. Bernd says:

    I happen to believe that the cost of this extensibility is badly underestimated

    I totally agree. Its gets me mad when I see devs declaring the d’tor of their newly created class virtual before any . On a code review when you ask them why they did this they usually say: "Because all d’tors must be virtual".

  8. XRay says:

    >> the derived class may have done "crazy" things inside that virtual method

    Usually there is some kind of "contract" (which should be documented but has some implicit points) which derived class must respect.

    If the method I call is called ReadData and the base class just reads data, the derived object clearly should not delete itself inside that method (unless explicitely allowed).

    An autodeletion inside a method which clearly should not autodelete is as evil as starting a thread which reads from char*p=(char*)(0) at a random time.

  9. Norman Diamond says:

    Some people argue that every method should

    > be virtual so that derived classes can

    > override the behavior of the base class in

    > ways the base class may not have anticipated.

    Agreed in principle (i.e. there could be rare exceptions).  The biggest reason for this is that the alternative is still the same old use of copy-paste in order to polymorph a function.

    > You can’t hold any locks when calling a

    > virtual method (else you may deadlock with

    > the derived class).

    I disagree.  For one thing, if the derived class writer doesn’t know what locks you hold, it’s their fault for deriving from you.  For another thing, if the derived class writer doesn’t know what locks they hold, they can deadlock with themselves and still kill you.

    > You have to be prepared for all sorts of

    > reentrancy because the derived class may

    > have decided to call MessageBox in its

    > implementation.

    Again I disagree.  You have to be prepared for all sorts of reentrancy because either (1) your program is reentrant by nature or (2) you’re running under Windows.  Even in C-style code with no classes, and no calls to MessageBox, your wndproc can still get reentered at surprising times.  I don’t think a derived class’s call to MessageBox adds much to this existing complexity.

    > You have to revalidate your state after the

    > virtual method returns because the derived

    > class may have done "crazy" things inside

    > that virtual method including possibly

    > destroying the object itself!

    Again I disagree.  First a bad example:

    If you’re a lung of a person who commits suicide, you’re dead.

    That’s a bad example because if you’re a lung then the person "has" you instead of "is" you.  So here’s a good example:

    If you’re a mammal of a person who commits suicide, you’re dead.

    Hope that’s clear ^_^

    Of course COM rules are completely different.

  10. Haroon Khan says:

    I think the Liskov Subsitution Principle is a good criteria to decide if the functions of a class are to be marked virtual or not. If a derived class can’t be seamlessly subsituted for a parent class or a sibling class, then the parent class is probably not a good candidate as parent class with virtual functions.

    In other words if the client object or function needs to know something about the internal mechanism of  the object of a derived class through a base class pointer, then the LSP is being violated, and using polymorphism is probably wrong.

    In my own (very limited) experience the LSP is pretty hard criteria to satisfy, but has been a nice guide to simplify my designs.

  11. Dave says:

    > "I guess I’m more concerned with the security aspects. A ‘contract’ or ‘intent of the method’ is out the window if the deriver’s intent is malicious."

    I certainly understand that approach for object reuse in the COM sense, because the environment can be hostile and the attacker misusing your component isn’t the one who owns the computer under attack.

    But this is source code. Why would someone be trying to derive a class with *malicious* intent? It’s easier to be directly evil in your own code, or by copying and modifying the original source code to change its behavior.

    Admittedly, someone might try to use your unmodified source as an obfuscating evil proxy for their own bad actions, but incompetence and carelessness has got to rate a lot higher as a cause of problems.

  12. Craig Ringer says:

    I’m not sure Brian’s argument about malicious code makes much sense. If someone manages to inject malicious code into your app using any of the aforementioned methods, the changes are that any state validation you do won’t help much. They can potentially patch the code doing the validation (except in the presence of DEP / W^X or equivalent), never return control to your app at all, or do almost anything else they want. In fact, we saw a good discussion of this issue just recently here in the New Old Thing with the post on using the return pointer as a caller security mechanism in DLLs.

    If you’re talking about an IPC interface like COM, then a certain level of paranoia and lack of reliance on documented "contracts" does make good sense. For in-process calls I don’t think it does.

    Personally I tend to include such state checks before and after virtual method calls, but they’re `const’-qualified and compiled in only for debug builds. This helps the subclass developer retain their sanity if they miss a documented requirement of the interface or my documentation is lacking, because the sanity checks will often detect their error. However, it imposes no large costs on the production builds. Cheap checks are often still included to make tracking down problems in deployed code easier.

  13. Sridhar says:

    When defining a method as virtual, there is an understanding on why this must be virtual.  The virtual method may be expected modify the state of the object.  By defining a post-condition (http://en.wikipedia.org/wiki/Postcondition) for a virtual method, we may have some control on what the method does.

    Other than Eiffel, is there programming langauges that support pre and post-conditions for virtual methods in some form or the other?  

    A good option would be to have attributes for pre and post conditions.

    class C1

    {

     private int a;

     [Pre-condition: a > 0 && a < 100;]

     [Post-condition: a > 0 && a < 1000;]

     protected virtual void ActOnA();

    };

  14. Jules says:

    I’m with Norman Diamond here.  I make almost all methods virtual, because at some later stage having a virtual method may be convenient.  (I like Roland’s pattern, and may adopt that in some cases.  Thanks.)

    I think the primary thing is to document what state the application may be in when the method is called.  If locks may be held, document that (assuming you don’t have some kind of global deadlock avoidance strategy).  My general feeling is that any methods designed to be called from the message loop should be reentrant anyway: you might want to stick a MessageBox in there for debugging purposes anyway, and coping with reentrancy isn’t normally difficult.  But if it isn’t reentrant, just document that the method cannot do anything that might result in a message being dispatched.  Sure, it limits the usefulness of being able to override the message, but it’s still better than having to go back and make a public interface change if you find you need to at a later stage.

    I think the key thing here is, make sure everyone knows what’s going on.  If you do that, it should work out OK.

    Just my experiences, which may be somewhat influenced by the fact that I work at a small shop (only 2 programmers).

  15. SS says:

    Interesting to compare java where everything (non static) is virtual unless you make it final, with c# where eveything isn’t virtual unless you explicitly make it so.

    I like the c# approach – I prefer deciding whether to open up a class as opposed to remembering to close it off.

  16. Jules says:

    >>I think the Liskov Subsitution Principle is a good criteria to decide if the functions of a class are to be marked virtual or not. If a derived class can’t be seamlessly subsituted for a parent class or a sibling class, then the parent class is probably not a good candidate as parent class with virtual functions.<<<

    I’d take it a step further.  If a derived class can’t be seamlessly substituted for a parent class, it shouldn’t be a subtype.  Make it its own type, using private inheritance (or using a ‘has-a’ type of relationship with the other class), and add new methods to expose any functions of the parent class that are really necessary.  That way you get the protection of the type system preventing you from using it in a situation that won’t work.

  17. ac says:

    I’m 100% with Raymond on this one, and believe that those who believe that every function or class should be virtual ("to support reuse", what a lame excuse) are not making effective programs (neither easy to maintain nor fast to execute). When they make programs for themself, I guess they see nothing wrong. Still I wouldn’t like them in my team.

    All these rules like "every function should be virutal" or "all variables should be private" are plainly bad. A lot of what was preached in multitude of books since C++ got popular came from people who never developed anything bigger than "hello world".

    When I’m by C++ problems, I consider the whole iostream C++ standard library a major flop, that demonstrats a lot of problems inherent in C++. Stroustrup should have developed C++ a little more to support something better.

  18. KiwiBlue says:

    @Jules:

    The "Roland’s pattern" is commonly known as "template method". See http://en.wikipedia.org/wiki/Template_method_pattern (or pick up the GOF book :)

  19. "Every function should be virtual" is an appealing opinion, but it is also very idealistic – which doesn’t mix well with the highly pragmatic nature of C++ itself.

    Java, Smalltalk, Ruby and other languages which to various extents aspire to be "fully" object oriented are a different beast altogether – functions being "virtual" by default (or that being the only option) makes sense in those languages.

    When programming in C++ I use virtual functions pretty much exclusively to define interface-type (in the java/C# sense) classes, most of which are abstract. IMHO that’s the single most useful application of inheritance – otherwise I do just as well without. Even the most trivial of textbook examples emphasize the value of being able to treat apples and oranges as fruit.

    Slightly off-topic, I just have to make a point: C++ isn’t a very dynamic language. Violent attempts to make it behave and seem more dynamic than it really wants to will only make you frustrated and unhappy. Mmkay?

  20. Jules says:

    >>> I totally agree. Its gets me mad when I see devs declaring the d’tor of their newly created class virtual before any. On a code review when you ask them why they did this they usually say: "Because all d’tors must be virtual". <<<

    I *always* make destructors virtual (except for classes that are used in a completely private context).  There is a very good reason for doing this: if a subclass of the class is added later, and an instance is used as a substitute of a parent class instance, it is possible for the wrong destructor to be called.  The slightest chance of that happening is truly bad.

  21. Jules says:

    >>When programming in C++ I use virtual functions pretty much exclusively to define interface-type (in the java/C# sense) classes, most of which are abstract. IMHO that’s the single most useful application of inheritance – otherwise I do just as well without.<<<

    I’ve been experimenting with writing systems in this kind of style lately, and have found the results quite interesting.  I’ve also started using factory objects to create all my objects.  Some of the results have been useful.  Other times it has cost me development time.  I think soon I’ll know when it is best to use the technique and when it isn’t.

  22. C-- says:

    Its gets me mad when I see devs declaring the d’tor of their newly created class virtual before any . On a code review when you ask them why they did this they usually say: "Because all d’tors must be virtual".

    Can Visual C++’s class generator maybe be the cause? All dtors it generates are virtual.

    Maybe someone@ms thought: "Gosh, somebody might want to override this, so I’ll make it virtual."

  23. Sean W. says:

    I’d like to point out that this problem, in C++, is both simpler and more complicated than it seems at first.  Consider these (legal C++) declarations:

    class A { … };

    class B : public virtual A { … };

    Regardless of what your intentions were with class A, regardless of what semantics you might have wanted it to have, every one of its methods are treated as virtual by class B.  So the choice of "virtual or not" in C++ is a bit misleading:  Anybody who really wants to can subvert your decision, no matter how wise your decision may have been.

    Because of this, I tend to lead more toward non-virtual than toward virtual (in fact, in light of the ability to do this, declaraing every method of a given class as "virtual" is  silly and meaningless).  Since my choice can be overridden anyway by anyone so inclined, I treat the "virtual" keyword as a hint to other programmers as to the intent of my design:  Methods without "virtual" were probably not intended to be overridden, while any method marked "virtual" has the full blessing of the original designer to be overridden.

  24. GregM says:

    Sean, virtual base classes are different than virtual functions.  Virtual base classes are used for multiple inheritance when those base classes are derived from a common base.  This makes the derived class only contain a single instance of that common base class.

    There is no way that deriving a class from A can make a function call on a pointer to A from a plain function to a virtual function.  The compiler generates completely different code for the two different types of calls.

  25. Norman Diamond says:

    Thursday, September 14, 2006 6:36 PM by Sean W.

    > perhaps "class A : common public B" would

    > have been a better term,

    The C/C++ philosophy is to reuse keywords as much as possible instead of defining new ones, deriving new uses for base keywords, but implementing new meanings that don’t derive from the old meanings.

    But you’re right that this reuse of "virtual" was a bit too counterintuitive in its meaning.

    So they should have called it "class A : static public B"  ^_^

  26. Sean W. says:

    Hmm, you’re right, Greg.  My mistake.

    (Or possibly a lousy word choice on the part of the C++ designers; perhaps "class A : common public B" would have been a better term, given what the "virtual" keyword does in that circumstance.  But, since I don’t use multiple inheritance, this explains why I’ve never found a need to stick a "virtual" in there before.)

    Still, I think the rationale that results from that misconception was pretty good, and I still follow that model in my own code.  If you don’t mark a method as virtual, anybody who really *wants* to get around that limitation can usually find ways to do so (even if not exactly the same as if it had been marked virtual).  I really think virtual should be reserved for methods where you’re giving your approval to other people to override them, not simply "just in case" (which can and does cause problems later in the design).  Everything in moderation, "virtual" included.

  27. Centaur says:

    So they should have called it "class A : static public B"  ^_^

    As if five different meanings of "static" were not enough :)

  28. Bernd says:

    >I *always* make destructors virtual (except for classes that are used in a completely private context).

    >There is a very good reason for doing this: if a subclass of the class is added later, and an instance is used as a substitute of a parent class instance, it is possible for the wrong destructor to be called.  The slightest chance of that happening is truly bad

    Many classes don’tr even need an d’tor. If a class just holds two doubles it doesn’t need a d’tor

    Of course in most cases it is totally right to declare a dtor virtual and therefor the claswizard has a good default by making all d’tor virtual. But not for every class. Hey it’s C++ it’s about having the choice! Its just that I would like people _think_ before they declare something virtual.

    Thats what we’re paid for :-)

  29. Sloppy BASIC programmer says:

    Thinking is laborious.

  30. albertito (aka keiko) says:

    hi,

    I’m a Spanish student and I have read your comments and I’d like to comment anyone have thought in this sentence:

    >>> "… that needs to be designed and tested." <<<

    We must remember that anything we do, it must tested. We, as software engineers, mustn´t only in the design; and we must think in the consequences, such as testing. In this stage, we have to test the security in our applications and other type of tests; and anything type of test means spending much money.

    Of course, we mustn’t forget that everything has more point of view and nothing has a only solution.

    Att,

    Alberto.



    Albertito (aka keiko)

    Página Personal: http://atetinho.googlepages.com

    Blog: http://ifelsedeveloper.blogia.com

    C.A.S: http://www.ajedrezsanturtzi.com

Comments are closed.

Skip to main content