CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 1 of 1
  1. #1
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470

    C++ Polymorphism: How to use virtual functions?

    Q: How do we use virtual functions?

    A: The FAQ "What is polymorphism?" covers why we have virtual functions. In this FAQ, I want to examine how we use them - in Scott Meyers's words "say what you mean, understand what you are saying".

    There are nine ways that you can declare a virtual function: it can be plain, pure or pure-with-a-body. Each of these three can be declared public, private or protected. Thus, nine declarations, but what does each one say? What do we mean when we declare a private, pure virtual function?

    "Hold on", I hear a voice from the back cry, "what do you mean by pure-with-a-body?". Well, it's one of those lesser-known facts of the language that a pure virtual function can have a definition (or body). After all, the pure specifier (the "= 0" bit) only says that this class cannot be instantiated - it does not say "this function cannot be implemented here". One of the simplest ways of making a class abstract (cannot be instantiated) is to declare the destructor pure - but destructors simply have to have a body.

    So, let's look at the various declarations and see what the programmer who uses each is saying. The first thing to point out is that, since the virtual mechanism works down inheritance hierarchies, there is not usually much difference in meaning between a public function and a protected one - it's just a matter of outside visibility rather than "meaning" in the sense that I'm covering it here.


    Plain Old Virtual Functions: Basically, these are saying "this function has a default implementation. If you do not override it, then the default will be used."

    If the function is non-private, then derived classes may also use it as part of their processing.

    Code:
    class foo
    {
    public:
      virtual void povf(); // has a body somewhere
    };
    
    class bar : public foo
    {
    public:
      void povf()
      {
        foo::povf();
        // do some more processing
      }
    };
    So here, foo::f() does something (probably involving foo's private data). The function is virtual because the programmer recognised that derived classes may have extra work to do with their own private data. If bar had nothing extra to do, then it could just ignore povf and let the default action happen.

    A private Plain Old Virtual Function, on the other hand, cannot be re-used by derived classes. It's saying "here is a default action: if you choose to override it, you may not incorporate into your override". Ok, let's admit it, the need for this is rare, but it's not unknown.


    Pure virtual functions:

    These are saying "here is behaviour that derived classes must implement."

    If there is no body for the function, then it is also saying "there is no sensible implementation for this function at this level." In other words, the class that declares the function is at a high level of abstraction and contains insufficient specialised information to provide an implementation. For example:

    Code:
    class bird
    {
    public:
      virtual void fly() = 0;  // No body
    };
    At the level of "bird", there is no way that it can implement fly(), since we don't know what type of bird we really are. It's not until we've derived "sparrow" and "albatross" from bird that we can supply an implementation.


    ---- A brief digression

    By the way, on this definition, emus, ostriches, kiwis, etc. are not birds. Class bird defines the ability to fly as a prerequisite for "bird-hood". Do not be tempted to do this:


    Code:
    class ostrich : public bird
    {
    public:
      void fly()
      {
        // throw an exception or something
      }
    };

    This is bad programming practice. Public inheritance means IS-A. If your derived class cannot implement a virtual function in its base class's interface, then it is not IS-A, and you should look at reviewing the design. So, to count ostriches as birds, we need:


    Code:
    class bird
    {
    public:
      // something
    };
    
    class flying_bird : public bird
    {
    public:
      virtual void fly() = 0;
    };

    now, "ostrich" derives from "bird", while "sparrow" derives from "flying_bird".

    ---- End of digression


    How does access control affect this? Well, basically, there's no point in declaring a pure virtual function with no body in the protected section. The only reason for making it protected would be to allow a derived class to call it, but there's nothing to call, so make it public or private depending on whether or not the outside world needs to call it.

    Pure virtuals that do have a body, on the other hand are saying: "here is a default implementation. If you wish to use it, you must do so explicitly". Note the difference here compared with the Plain Old Virtual Function. With a POVF, you might get the default action by accident, because you forgot to write your override. By making the function pure, you can only get the default implementation deliberately:

    Code:
    class foo
    {
    public:
      virtual void pvfwb() = 0;
    };
    
    void foo::pfvwb()
    {
      // something
    }
    
    class bar1 : public foo
    {
      // ...
    };
    
    class bar2 : public foo
    {
    public:
      void pvfwb()
      {
        foo::pvfwb();
      }
    };
    "bar1" does not override pvfwb(), so it remains an abstract class. "bar2", however, reuses the default implementation deliberately. The meaning of the coder is clear.

    And access control? Well, there seems little point in making one of these private. Only the class itself (and its friends) could call it, and then it would have to qualify the call:

    Code:
    class foo
    {
    public:
      void f() { foo::g(); }
    
    private:
      virtual void g() = 0;
    };
    
    void foo::g()
    {
      // ...
    }
    There seems little point in making it virtual, in this case.

    Finally, what do non-virtual functions say?

    A non-virtual function says "the implementation of this behaviour does not change in derived classes". A non-virtual function is invariant over specialisation: derived classes inherit behaviour and implementation. Do not override a non-virtual function in a derived class - you will pay dearly in lost maintenance time if you do.


    Last edited by Andreas Masur; July 24th, 2005 at 05:29 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured