Actor 5 begins with the non-polymorphic Actor 4 example and takes it through two evolutionary steps. First, it makes the program polymorphic by making the display function virtual, demonstrating basic polymorphism and the limitations of non-member functions. The second step introduces a dynamically-organized data structure. The program inserts instances of Person, Actor, and Star into the structure, demonstrating that polymorphism can select and run the correct function even when an object's precise class-type is unknown at compile-time.
virtual, virtual functionsThe chapter began with a list of five programming elements necessary to enable polymorphism:
As a practicing computer scientist, no one will ask you to list these elements. However, reading and understanding a program's behavior are expected skills. 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 the 5th requirement.
virtual, Actor 5, virtual functionsMany 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 them polymorphic.
|
![]() |
virtual satisfies requirement 5, activating polymorphism. Adding the keyword is a deceptively simple step, yet it introduces complex structures and operations into a compiled program. Fortunately, polymorphism hides this complexity, allowing programmers to use it without concern about how C++ implements it. Requirements 1 and 2 imply that polymorphism only works when a program has two or more inheritance-related classes with at least one overridden function. The Actor 5 example makes the Person class display function virtual, automatically making the Actor and Star display functions virtual. Nevertheless, I typically add the keyword to the subclass's functions for clarity.
The following figure illustrates a program creating an instance of Star and calling its display function. The display functions are chained, so the calling sequence runs from the bottom of the inheritance hierarchy upwards to Person, which calls the display functions of its parts.
#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;
}
friend function, which is not a member of the befriending class, preventing function overriding (not to be confused with overloading).
| s2->display(); | cout << *s2 << endl; |
|---|---|
Person: John Wayne Address: California, 123 Palm Springs Date: 1960/12/25 Actor: Cranston Snort Star: 5e+07 |
Person: John Wayne Address: California, 123 Palm Springs Date: 1960/12/25 |
| (a) | (b) |
friend function and therefore cannot be a member or polymorphic. Actor 5 adds labels to the output statements, correlating the output to a specific class.
virtual satisfies the five polymorphism requirements. So, despite s2 being a Person pointer, the statement s->display(); runs the Star display function, which is chained upwards to the overridden Actor and Person functions.cout << *s2 << endl; only runs the Person inserter, which is chained to the Address and Date classes.The second, extended Actor 5 presents polymorphism in a slightly more authentic setting by storing objects in a dynamic data structure called a linked list. Linked Lists consist of nodes containing the data and one or more pointers called links that bind the nodes together. Lists are among the most flexible structures with numerous implementations and behaviors. Actor 5 uses a circularly linked list, formed like a necklace, with each node pointing or linked to the next and the last node pointing to the first.
| A circularly-linked list with data | A logically empty list |
|---|---|
![]() |
![]() |
The second Actor 5 example implements the circularly linked list named CList. Although library-grade data structures can generally store any data type, for simplicity, CList is limited to storing instances of Person and its subclasses. There are many varieties of linked lists, but, to demonstrate polymorphism, CList organizes the data alphabetically by name, implying that the storage order is not necessarily the same as the insertion order. The class's details are better suited to a data structures and algorithms course. Fortunately, we can treat it as a "black box," ignoring its implementation details and focusing on its public interface.
CList()~CList()Person* insert(Person* p)Person* search(Person* key)Person* remove(Person* key)nullptr.void list();
string getName()
{
return name;
}
To complete the polymorphism example, Actor 5 creates a CList and adds three different kinds of Person objects to it. The insert function maintains the objects in alphabetical order, so when the example prints them with the list function, the output order may differ from the insertion order. Nevertheless, polymorphism correctly calls the display method 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 | The CList class specification |
| CList.cpp | CList.cpp | CList class member functions | |
| Actor5_poly.cpp | Actor5_poly.cpp | The final Actor 5 example driver |