CS 4448 - Spring 1998
Object-Oriented Programming and Design

Chapter 10 Polymorphism - "The ability to operate on and manipulate different derived objects in a uniform way is called polymorphism."

 

10.1 Polymorphic Behavior

From the previous chapter, inheritance allows us to create derivatives of a base class that retain the base class functionality.  Inheritance provides the programmer a means to modularize and reuse functionality between related objects and a way to encapsulate data.  To extend the functionality, readability and reusability of classes, a programmer may utilize polymorphism to make transparent the use of related objects.  This design concept in no way interferes with the implementation of an inheritance hierarchy or inheritance principles, but instead enhances class functionality.  By utilizing polymorphic behavior to specify a design interface for a base class, client applications are able to utilize all derivatives of a the base class in a generic manner.
 
For example, if we wish to create an interface for objects which have attributes reflective of shapes, we can design a Shape class which exhibits polymorphic behavior.  The Shape class will define the interface for all classes we chose to derive from it such as: a Circle, Rectangle, or Triangle classes.
 

10.2 Virtual Functions

Inheritance alone does not allow access to derived member functions through a base class pointer. To access derived class functionality via base class pointers we must utilize polymorphism through virtual functions and the base class interface. Using an array of base class Shape pointers to reference Circle, Rectangle, and Triangle objects that implement virtual functions defined in the base class, we can operate on the derived objects.
 
It is important to note that while polymorphism allows us to manipulate derived class via a base class reference, only data which exists in the base class can be seen. The memory for the derived object exists but is not accessible until the base class pointer is cast into an object of the correct derived type. It does, however, allow us to modify the data within the base class using derived, virtual methods.
 
For example, if the Shape base class defines a virtual method draw() to display Shapes, derived classes may implement this method in a manner suitable for the particular class they are implementing. A draw() method for a Circle object would be significantly different from one implemented by a Rectangle object. So while each class only has access to the base class data, each class may utilize it differently through polymorphism and virtual functions defined by the base class.
 

10.3 Dynamic Binding

To ensure the correct method for a given class is used when acting upon objects referenced through a base class pointer, a programming language must allow for dynamic or run time binding. By utilizing dynamic binding a compiler can generate a virtual table, or v-table, representing pointers to all the virtual functions associated with a class. The v-table ensures the correct implementations for methods invoked on base class pointers which actually point to derived class objects are called.
 
For example, a v-table is created for all the virtual methods of the Shape, Circle, Rectangle and Triangle classes mentioned above. When invoking the virtual draw() method on a base class pointer a v-table lookup is performed based on the actual class represented by the pointer. If the pointer references a Circle object, the draw() method for a Circle is looked up in the Circle's v-table and executed. Similarly for the other Shape class derivatives.
 

10.4 Abstract Class

To enforce program correctness and portability when utilizing polymorphism, a base class can be made abstract. Abstract classes define a specific interface which must be adhered to by derived classes. This prevents erratic, unpredictable behavior which can occur when virtual functions implemented by the base class are invoked on derived classes which do not implement a suitable alternative.
 
For a class to be considered abstract it must implement at least one method which is "purely virtual" as defined by the language in which it is written. These purely virtual methods define the base class template which must be utilized by all of its derived classes less they, themselves, become abstract. The abstract definitions of the base class do not restrict derived classes from implementing other virtual or non-virtual methods. Note, however, that only these purely virtual functions can be utilized polymorphically when referencing derived class functionality via a base class pointer.
 
Note, since abstract classes define an interface and no useful functionality they cannot be instantiated themselves.
 

10.5 Polymorphism and Class Management

When creating a class which utilizes polymorphism, its constructor cannot be made virtual but its destructor must be so. The constructor cannot be virtual to ensure the correct order of memory allocation is met when an object is created. In other words the derived class constructor must be called following that of the base class to ensure any necessary initialization of data represented in the base class takes place. For the same reasoning behind the non-virtual constructor, a class which defines virtual functions must define a destructor which is itself virtual to ensure proper memory de-allocation of instantiations of the class. Since the type of an object which utilizes polymorphism cannot be determined at compile time it is necessary that the correct virtual implementation of a destructor be called when recovering memory associated with derived objects.
 

10.6 Dynamic Casting

Unfortunately polymorphism alone does not allow us to fully utilize the functionality of derived classes through base class pointers. If we wish to access other non-virtual methods and data associated with a derived class, we must first convert the base class reference to the object to the correct derived type (narrowing or downcasting) before it can be manipulated. Since the type checking can be performed dynamically the narrowing of the object will only occur if the cast is of the correct derived type otherwise a system specific error will occur.
 

Chapter Summary

Polymorphism provides a means for better OO design, encapsulation and portability of program code. By defining a design interface through an abstract class a programmer can ensure correct behavior, conformity and a logical design structure when implementing derived objects of the class.
 
Virtual methods provide a mechanism for accessing derived class functionality generically when referenced via a base class pointer.
 
To access data and non-virtual methods within a derived class which is currently referenced through a base class pointer the class must first be cast to the correct derived type.
 
Abstract classes define a template which must be implemented by derived classes to ensure a uniform behavior across a design hierarchy.
 
Polymorphism enforces correct behavior regarding memory allocation and de-allocation. Constructors cannot be defined as virtual to ensure data representing a base class is properly initialized. Furthermore, destructors of classes implementing virtual methods must themselves be virtual to ensure the correct cleanup of derived instance data.

Copyright © University of Colorado. All rights reserved.
Revised: March 31, 1998