Polymorphism
(implemented in C++ with
virtual
functions) is the third essential feature of an object-oriented programming
language, after data abstraction and inheritance.
It
provides another dimension of separation of interface from implementation, to
decouple
what
from
how.
Polymorphism allows improved code organization and readability as well as the
creation of
extensible
programs that can be “grown” not only during the original creation
of the project, but also when new features are desired.
Encapsulation
creates new data types by combining characteristics and behaviors.
Implementation hiding separates the interface from the implementation by making
the details
private.
This sort of mechanical organization makes ready sense to someone with a
procedural programming background. But virtual functions deal with decoupling
in terms of
types.
In the last chapter, you saw how inheritance allows the treatment of an object
as its own type
or
its base type. This ability is critical because it allows many types (derived
from the same base type) to be treated as if they were one type, and a single
piece of code to work on all those different types equally. The virtual
function allows one type to express its distinction from another, similar type,
as long as they’re both derived from the same base type. This distinction
is expressed through differences in behavior of the functions you can call
through the base class.
In
this chapter, you’ll learn about virtual functions starting from the very
basics, with simple examples that strip away everything but the
“virtualness” of the program.
Evolution
of C++ programmers
C
programmers
seem to acquire C++ in three steps. First, as simply a “better C,”
because C++ forces you to declare all functions before using them and is much
pickier about how variables are used. You can often find the errors in a C
program simply by compiling it with a C++ compiler.
The
second step is “object-based” C++.
This means that you easily see the code organization benefits of grouping a
data structure together with the functions that act upon it, the value of
constructors and destructors, and perhaps some simple inheritance. Most
programmers who have been working with C for a while quickly see the usefulness
of this because, whenever they create a library, this is exactly what they try
to do. With C++, you have the aid of the compiler.
You
can get stuck at the object-based level because it’s very easy to get to
and you get a lot of benefit without much mental effort. It’s also easy
to feel like you’re creating data types – you make classes, and
objects, and you send messages to those objects, and everything is nice and neat.
But
don’t be fooled. If you stop here, you’re missing out on the
greatest part of the language, which is the jump to true object-oriented
programming. You can do this only with virtual functions.
Virtual
functions
enhance the concept of type rather than just encapsulating code inside
structures and behind walls, so they are without a doubt the most difficult
concept for the new C++ programmer to fathom. However, they’re also the
turning point in the understanding of object-oriented programming. If you
don’t use virtual functions, you don’t understand OOP yet.
Because
the virtual function is intimately bound with the concept of type, and type is
at the core of object-oriented programming, there is no analog to the virtual
function in a traditional procedural language. As a procedural programmer, you
have no referent with which to think about virtual functions, as you do with
almost every other feature in the language. Features in a procedural language
can be understood on an algorithmic level, but virtual functions can be
understood only from a design viewpoint.