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:
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.
if this referred to the local class definition?> (call height b)
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.
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?