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.
The chapter began with a list of five elements that classes and programs must provide to enable polymorphism:
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.
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.
virtual void display() { cout << name << endl; addr.display(); // composition if (date != nullptr) // display if available date->display(); // aggregation }
virtual
. Nevertheless, I typically add the keyword to the subclass's functions for clarity.
#include "Star.h" #include <iostream> #include <string> using namespace std; int main() { Person* s2 = new Star("John Wayne", "Cranston Snort", 50000000, "123 Palm Springs", "California"); s2->setDate(1960, 12, 25); s2->display(); cout << endl; cout << *s2 << endl; return 0; }
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) |
friend
function and therefore cannot be a member. Please notice that this example instantiates a subclass (Star) and upcasts it to a superclass (Person).
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.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.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 |
---|---|
![]() |
![]() |
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:
CList()
~CList()
Person* insert(Person* p)
Person* search(Person* key)
Person* remove(Person* key)
nullptr
.void list();
The second version only requires adding a getter function to the Person class.
string getName() { return name; }
private
, necessitating a getter function. A more general list assumes the data is orderable, typically by overloading operator<.
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:
#include "Person.h" #include "Actor.h" #include "Star.h" #include "CList.h" // (a) #include <iostream> #include <string> using namespace std; int main() { CList people; // (b) Star* s = new Star("John Wayne", "Cranston Snort", 50000000, // (c) "123 Palm Springs", "California"); s->setDate(1960, 12, 25); people.insert(s); Actor* a = new Actor("Dilbert", "Wally", "2401 Edvalson", "Ogden"); // (c) a->setDate(1961, 1, 1); people.insert(a); Person* p = new Person("Alice", "115 Elm", "Ogden"); // (c) p->setDate(1975, 5, 22); people.insert(p); people.list(); // (d) return 0; }
Much of the Actor 5 demonstration code is copied or adapted from the Actor4.cpp example.
Version | View | Download | Comments |
---|---|---|---|
All | Address.h | Address.h | Unchanged from Actor 4 |
Date.h | Date.h | ||
Person.h | Person.h | Added getName() (for CList); made display virtual | |
Actor.h | Actor.h | Unchanged from Actor 4 | |
Star.h | Star.h | ||
1 | Actor5.cpp | Actor5.cpp | Adapted from Actor4.cpp |
2 | CList.h | CList.h | |
CList.cpp | CList.cpp | ||
Actor5_poly.cpp | Actor5_poly.cpp |