winner = olaf1; // assign derived to base object
In this case, the program uses the implicit overloaded assignment operator:
TableTennisPlayer & operator=(const TableTennisPlayer &) const;
Again, a base-class reference refers to a derived-class object, and just the base-class portion of olaf1 is copied to winner.
Inheritance: An Is-a Relationship
The special relationship between a derived class and a base class is based on an underlying model for C++ inheritance. Actually, C++ has three varieties of inheritance: public, protected, and private. Public inheritance is the most common form, and it models an is-a relationship. This is shorthand for saying that an object of a derived class should also be an object of the base class. Anything you do with a base-class object, you should be able to do with a derived-class object. Suppose, for example, that you have a Fruit class. It could store, say, the weight and caloric content of a fruit. Because a banana is a particular kind of fruit, you could derive a Banana class from the Fruit class. The new class would inherit all the data members of the original class, so a Banana object would have members representing the weight and caloric content of a banana. The new Banana class also might add members that apply particularly to bananas and not to fruit in general, such as the Banana Institute Peel Index. Because the derived class can add features, it’s probably more accurate to describe the relationship as an is-a-kind-of relationship, but is-a is the usual term.
To clarify is-a relationships, let’s look at some examples that don’t match that model. Public inheritance doesn’t model a has-a relationship. A lunch, for example, might contain a fruit. But a lunch, in general, is not a fruit. Therefore, you should not derive a Lunch class from the Fruit class in an attempt to place fruit in a lunch. The correct way to handle putting fruit into a lunch is to consider the matter as a has-a relationship: A lunch has a fruit. As you’ll see in Chapter 14, that’s most easily modeled by including a Fruit object as a data member of a Lunch class (see Figure 13.3).
Figure 13.3. Is-a and has-a relationships.
Public inheritance doesn’t model an is-like-a relationship—that is, it doesn’t do similes. It’s often pointed out that lawyers are like sharks. But it is not literally true that a lawyer is a shark. For example, sharks can live underwater. Therefore, you shouldn’t derive a Lawyer class from a Shark class. Inheritance can add properties to a base class; it doesn’t remove properties from a base class. In some cases, shared characteristics can be handled by designing a class encompassing those characteristics and then using that class, either in an is-a or has-a relationship, to define the related classes.
Public inheritance doesn’t model an is-implemented-as-a relationship. For example, you could implement a stack by using an array. However, it wouldn’t be proper to derive a Stack class from an Array class. A stack is not an array. For example, array indexing is not a stack property. Also a stack could be implemented in some other way, such as by using a linked list. A proper approach would be to hide the array implementation by giving the stack a private Array object member.
Public inheritance doesn’t model a uses-a relationship. For example, a computer can use a laser printer, but it doesn’t make sense to derive a Printer class from a Computer class, or vice versa. You might, however, devise friend functions or classes to handle communication between Printer objects and Computer objects.
Nothing in the C++ language prevents you from using public inheritance to model has-a, is-implemented-as-a, or uses-a relationships. However, doing so usually leads to programming problems. So let’s stick to the is-a relationships.
Polymorphic Public Inheritance
The RatedPlayer example of inheritance is a simple one. Objects of the derived class use the base-class methods without change. But you can encounter situations in which you want a method to behave differently for the derived class than it does for the base class. That is, the way a particular method behaves may depend on the object that invokes it. This more sophisticated behavior is termed polymorphic (“having many forms”) because you can have multiple behaviors for a method, depending on the context. There are two key mechanisms for implementing polymorphic public inheritance:
• Redefining base-class methods in a derived class
• Using virtual methods