How Polymorphism Works in C++

By Stephen R. Davis

As a fence straddler, C++ supports both early and late binding. Any given programming language can support either early or late binding based upon the whims of its developers. Older languages like C tend to support early binding alone. Recent languages like Java and C# support only late binding.

You may be surprised that the default for C++ is early binding. The output of the OverloadOverride program the way it appears is as follows:

We're in Student::calcTuition
We're in Student::calcTuition
Press Enter to continue...

The reason is simple, if a little dated. First, C++ has to act as much like C as possible by default to retain upward compatibility with its predecessor. Second, polymorphism adds a small amount of overhead to every function call both in terms of data storage and code needed to perform the call.

The founders of C++ were concerned that any additional overhead would be used as a reason not to adopt C++ as the system’s language of choice, so they made the more efficient early binding the default.

To make a member function polymorphic, the programmer must flag the function with the C++ keyword virtual, as shown in the following modification to the declaration in the OverloadOveride program:

class Student
{
  public:
    virtual void calcTuition()
    {
        cout << "We're in Student::calcTuition" << endl;
    }
};

The keyword virtual that tells C++ that calcTuition() is a polymorphic member function. That is to say, declaring calcTuition() virtual means that calls to it will be bound late if there is any doubt as to the run-time type of the object with which calcTuition() is called.

Executing the OverloadOveride program with calcTuition() declared virtual generates the following output:

We're in Student::calcTuition
We're in GraduateStudent::calcTuition
Press Enter to continue...

If you’re comfortable with the debugger that comes with your C++ environment, you really should single-step through this example. It’s so cool to see the program single-step into Student::calcTuition() the first time that fn() is called but into GraduateStudent::calcTuition() on the second call. You cannot truly appreciate polymorphism until you’ve tried it.

You need to declare the function virtual only in the base class. The “virtualness” is carried down to the subclass automatically. Sometimes, however, programmers follow the coding standard of declaring the function virtual everywhere (virtually).