The object-oriented paradigm has three defining characteristics: encapsulation, inheritance, and polymorphism. Encapsulation and inheritance can stand independently, but polymorphism requires inheritance. Although polymorphism is a potent tool, it isn't easy to fully appreciate its power outside large, complex programs. Therefore, this semester focuses on its syntax and behavior while forgoing more useful examples. Rest assured that subsequent courses will build on this basic foundation. Chapter 12 covers several topics, but they all lead up to and revolve around a central theme: polymorphism.
polymorphism is just a way of choosing which function to execute when multiple functions match the same function call.
Three additional terms offer different perspectives on polymorphism, helping to build a deeper understanding of its behavior:The text uses the simplified problem of drawing shapes to illustrate a situation where allowing a single function call to match multiple functions can simplify programming a solution. The illustration begins by posing the problem and outlining a solution that doesn't use polymorphism. After detailing the concept necessary for polymorphism, the text revisits the problem and illustrates how a polymorphic solution simplifies the program. The chapter concludes with an authentic example that is awkward and challenging to solve without polymorphism.
The shape problem allows a user to select a provided shape and draw it on the screen. It must perform three distinct tasks:
class Circle
{
public:
void draw();
}; |
cout << "C:\tCircle" << endl;
cout << "R:\tRectangle" << endl;
cout << "T:\tTriangle" << endl;
cout << "Please choose a shape: ";
char choice;
cin >> choice;
cin.ignore();
Circle* c = nullptr;
Rectangle* r = nullptr;
Triangle* t = nullptr;
switch (choice)
{
case 'C' :
case 'c' :
c = new Circle(...);
break;
case 'R' :
case 'r' :
r = new Rectangle(...);
break;
case 'T' :
case 't' :
t = new Triangle(...);
break;
} |
switch (choice)
{
case 'C' :
case 'c' :
c->draw();
break;
case 'R' :
case 'r' :
r->draw();
break;
case 'T' :
case 't' :
t->draw();
break;
} |
class Rectangle
{
public:
void draw();
};
|
||
class Triangle
{
public:
void draw();
}; |
||
| (a) | (b) | (c) |
The previous figure illustrates a situation where multiple classes define functions with the same signature. Object-oriented programs specify the correct function by binding the call to an object with either the dot or arrow operator, which is generally adequate. However, in the shape example, the program "doesn't know" which function to call until the user selects a shape, which only happens after the program starts running. So, the program must be sufficiently dynamic to choose the correct draw function from several choices while it is running. The current program does this, but at the expense of passing many arguments. Now, imagine that the user wishes to add another shape to the program: an Ellipse. What code must programmers modify?
When the compiler processes a function, it generates the machine code from the function body, which the operating system loads into memory with the rest of the program when it runs. Normally, the compiler binds a function call to the function's machine code by generating instructions to jump to the code's memory location. The program executes the machine code before returning to the calling point. So, for the function calls
c->draw();r->draw();t->draw();the compiler binds statement 1 to the Circle draw function, statement 2 to Rectangle, and statement 3 to Triangle. Programmers use three terms to name and describe this kind of binding:
The terms are synonyms, meaning the compiler can bind a function call to a specific set of machine instructions when it compiles the program. Compile-time binding is generally sufficient, but, as the shape-drawing problem illustrates, it is sometimes inconvenient and awkward. Polymorphism provides a more elegant, easier-to-maintain solution.
Polymorphism is active by default in Java programs, and programmers must explicitly deactivate it when it's not wanted. Conversely, before programmers can use polymorphism in a C++ program, they must activate it by satisfying five requirements.
The text previously described the first three requirements, but others are new, and roughly outline the remainder of the chapter.