Upcasting
In Chapter 14 you saw how an object can
be used as its own type or as an object of its base type. In addition, it can be
manipulated through an address of the base type. Taking the address of an object
(either a pointer or a reference) and treating it as the address of the base
type is called upcasting because of the way
inheritance trees are drawn with the base class at the top.
You also saw a problem arise, which is
embodied in the following code:
//: C15:Instrument2.cpp
// Inheritance & upcasting
#include <iostream>
using namespace std;
enum note { middleC, Csharp, Eflat }; // Etc.
class Instrument {
public:
void play(note) const {
cout << "Instrument::play" << endl;
}
};
// Wind objects are Instruments
// because they have the same interface:
class Wind : public Instrument {
public:
// Redefine interface function:
void play(note) const {
cout << "Wind::play" << endl;
}
};
void tune(Instrument& i) {
// ...
i.play(middleC);
}
int main() {
Wind flute;
tune(flute); // Upcasting
} ///:~
The function tune( ) accepts
(by reference) an
Instrument, but also without complaint anything derived from
Instrument. In main( ), you can see this happening as a
Wind object is passed to tune( ), with no
cast necessary. This is acceptable; the interface in
Instrument must exist in Wind, because Wind is publicly
inherited from Instrument. Upcasting from Wind to
Instrument may “narrow” that interface, but never less than
the full interface to Instrument.
The
same arguments are true when dealing with pointers; the only difference is that
the user must explicitly take the addresses of objects as they are passed into
the
function.