Two versions of Actor 5 demonstrate polymorphism. The first version begins with the Actor 4 example, and makes the display() function virtual, demonstrating basic polymorphism and the limitations of non-member functions. The second version adds authenticity by inserting instances of Person, Actor, and Star into a data structure named CList. A CList maintains an alphabetically ordered list of objects instantiated from Person and its subclasses. The order in which the list saves the objects may differ from the insertion order because it organizes them alphabetically, but polymorphism displays them correctly. The example presents some code fragments for each version in the discussion and provides links to the complete programs at the end of the section.
Basic Polymorphism: A Review
The chapter began with a list of five elements that classes and programs must provide to enable polymorphism:
Inheritance
Function overriding
A pointer or reference variable (polymorphism cannot operate through an automatic or local variable)
Upcasting
One or more virtual functions
As a practicing computer scientist, no one will ask you to list these elements. However, reading and understanding the behavior of a program is an expected skill. In the case of polymorphism, you must recognize the presence or absence of the five elements in a program and understand their impact on it. As you study the following changes to the Actor program, compare the changes to the list of requirements. A program must implement all five features to activate polymorphism.
The Actor 4 example has five classes, but only three, Person, Actor, and Star are related by inheritance and participate in polymorphism. The remaining two, Address and Date, don't participate in polymorphism and remain unchanged from the earlier example. Each class defines a display function with a void return type and an empty parameter list, satisfying requirement 2. So, Actor 4 classes have the first two "ingredients" for polymorphism, and the "Automatic" parts of the driver satisfy requirements 3 and 4. However, we must modify the example to achieve all the requirements.
Simple Polymorphism And Non-Member Functions
Many of polymorphism's requirements are intrinsic to the problem and its solution (1 and 2) or depend on the client program (3 and 4). However, a superclass with an overridden function is responsible for the fifth and final requirement. The Actor 5 example makes the display functions in the Person, Actor, and Star classes virtual, satisfying requirement 5 and making display polymorphic.
s2->display();
cout << *s2 << endl;
John Wayne
California, 123 Palm Springs
1960/12/25
Cranston Snort
5e+07
John Wayne
California, 123 Palm Springs
1960/12/25
(a)
(b)
Polymorphic behavior.
The Actor example has unrealistically maintained two output operations throughout its evolution - "real" programs typically choose one or the other - for two reasons. First, to demonstrate the distinctly different syntaxes required to use the display function and the overloaded inserter operator. The second reason is demonstrating the difference between polymorphic and non-polymorphic behavior. The display function is a member of each Actor class, while operator<< is a friend function and therefore cannot be a member. Please notice that this example instantiates a subclass (Star) and upcasts it to a superclass (Person).
Adding the "virtual" keyword to the display function prototype satisfies all five polymorphism requirements. So, despite s2 being a Person pointer, the statement s->display(); runs the Star display function, printing the first three output lines. The example chains the display functions upwards from Star to Actor (printing the fourth line) and from Actor to Person (printing the fifth line). The virtual keyword in the Actor 5 display is the cause for the difference between the display and operator<< output.
In contrast, C++ always implements the inserter as a non-member friend, which is non-polymorphic. Upcasting Star to Person in this example causes the inserter to print only the Person data. Compare the Actor 5 output shown here to the Actor 4 operator<< output.
When polymorphism is fully enabled, the function belonging to the instantiated object's class, not the pointer variable, runs. Alternatively, when polymorphism is not enabled, the function belonging to the pointer-variable's class runs. Therefore, the inserter function call runs the Person inserter, showing only the Person's name, address, and date, but none of the information stored in the subclass objects.
An Extended Polymorphism Example
The second, extended version presents polymorphism in a slightly more authentic setting using a dynamic data structure called a linked list. Data structures store data (often objects), organize them in some way, and provide a set of operations to maintain the list and access the data. Linked data structures consist of nodes containing the data and one or more pointers called links. The example uses a circularly linked list, one of the many linked list variations, formed by each node pointing or linked to the following node and the last node pointing to the first.
A circularly-linked list with data
A logically empty list
A circularly-linked list.
The list head does not store any data but acts as a "handle" to manipulate or "hang on to" the list. The "Data" in each node is a pointer to an instance of the Person class. A logically empty list consists of a header node without any data nodes, avoiding the boundary conditions encountered when inserting the first data node in the list or removing the last data node.
Although library-grade data structures can generally store any data type, the list used in this demonstration is limited for simplicity, only storing instances of the Person class. There are many variations on linked lists, and this variation organizes the data alphabetically based on the person's name. This organization implies that the list can reorder the stored objects. So, the storage order is not necessarily the same as the insertion order, making a more interesting demonstration of polymorphism.
The second Actor 5 example implements the circularly linked list as a supplier class named CList. While the class's details are more appropriate for a data structures and algorithms course, it presents a straightforward public interface consisting of six functions, which we explore through a set of function prototypes:
The second version only requires adding a getter function to the Person class.
The Actor 5 main creates a CList, adds three different kinds of Person objects to the list, and then prints all of the information for each object:
Downloadable Code
Much of the Actor 5 demonstration code is copied or adapted from the Actor4.cpp example.