Virtual Functions

Chapter: Virtual Functions

The Scheme-- object system provides an elegant and clear virtual function mechanism. Understanding the idea of virtual functions may be best accomplished by first exploring a simple example: START Consider the following hierarchy of classes where Tall is a superclass of Short:

(define Tall
  (class ()
    ()
    (Object)
    ([printHeight (method () (printf "tall~%"))]
     [height (method () (call printHeight this))])))
(define Short
  (class ()
    ()
    (Tall)
    ([printHeight (method () (printf "short~%"))]
     [height-super (method () (call height super))])))

Now observe the following results:

> (define a (Tall))
> (define b (Short))
> (call printHeight a)
tall
> (call printHeight b)
short

So far these are the results one would expect, we are simply calling the printHeight methods of each instance. Now consider this:


> (call height a)
tall
> (call height b)
short

The first call gives the result we would expect, but what about the call (call height b)? Let's trace through the calling sequence:

  1. The method height is called on the object b, which is of class Short.

  2. Since Short does not override the method height, the call is delegated to the superclass Tall.

  3. The height method is called, which in turn makes the following call, (call printHeight this).

  4. Because the variable this always refers to the original object, which in this case is b, the printHeight method of Short is called.

  5. The printHeight method of Short prints the string "short".

The key to the virtual function mechanism is in step 4. Because this is always bound to the instance from the originating call (step 1), the printHeight method as defined in Short is called rather than the printHeight method in Tall.

This makes sense because the object we are calling is b, and it should maintain the behavior of an instance of class Short, even if it called from a superclass. When we override a method such as printHeight we want calls to this to refer to the overriden method. In this example, we are using printHeight as a virtual function. We can think of printHeight as "virtual" because it takes on differing behavior depending on the instance type of our original object.

In the exmaple

> (call height a)
tall
> (call height b)
short

printHeight takes on two different behaviors depending on whether we call instance a or b.

Q. 5
What would happen in the example:
> (call height b)
if this referred to the local class definition?


Q. 6
Suppose we want to use A's printHeight method in a B object. Is this possible?


In Scheme--, we get virtual function behavior whenever we use the keyword this. Any method g can be virtual because the result of (call g this) from within a base class depends on whether any derived classes override g. Note that virtual behavior only becomes an issue when the original call invokes a method which in turn calls another method.

The following diagram illustrates the general case of virtual function calls. Arrows indicate the passing of calls between class instances.

In C++ the keyword virtual is required to indicate that we want the function to be overriden in calls from the superclass. That is not necessary in Java.

Look at the file to see how the above Scheme example might be implemented in C++.

If you'd like you can copy the file virtual.cpp into your directory and compile it with the command
g++ virtual.cpp -o virtual, and then run the executable to see the results for yourself.

Q. 7

What is the output of the program if printHeight is not declared to be virtual?


What about Java? Virtual.java. Do we need a keyword like virtual?


rhyspj@gwu.edu