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 - "To
implement a design hierarchy that supports polymorphism,
the base class must specify the design interface
for its derived classes."
- 10.2 Virtual Functions - "C++
provides polymorphic behavior through the
use of inheritance and virtual
functions."
- 10.3 Dynamic Binding - The ability to
determine an object's type at run time.
- 10.4 Abstract Class - A class of this type
is used to specify a common design interface for
its derived classes.
- 10.5 Polymorphism and Class Management -
Polymorphic class memory allocation and
de-allocation.
- 10.6 Dynamic Casting - "[Allows
clients to] use a pointer or reference to the base
class type to operate on instances of derived
classes."
-
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