| < Day Day Up > |
|
Of all the object-oriented topics, the proper definition and use of inheritance causes the most controversy in both the academic and professional communities. Essentially, getting inheritance right is not easy.
There are two primary uses of inheritance: as an enabler of advanced, object-oriented reasoning about the structural aspects of a program, and second, as a way to incrementally evolve code.
One use of inheritance is as an aid to the programmer helping them reason about the nature or structure of a program. When using inheritance in this fashion a programmer will think of class types and their subtypes and how these conceptual entities fulfill their role in an application’s design. Using inheritance in this way is perhaps the quintessential difference between the object-oriented programmer and the procedural programmer. Inheritance is the enabler of object-oriented programming. Understanding the use of inheritance in this way results in the “light bulb” going on in a programmer’s head.
Another use of inheritance is to achieve incremental code evolution. For instance, when Employee inherits from Person, the only thing an Employee class must do is implement the functionality that differentiates itself from its superclass. Inheritance in this regard facilitates differential or incremental program development. The Person class represents a generalization, where as the Employee class represents a specialization. Using inheritance in this fashion is fine so long as subclasses are in fact providing a strict specialization of the superclass. However, when you override a base class function in a derived class, the only “rule” C++ enforces is that the derived class function have the same name (signature) as the base class function it overrides. This opens the possibility of unexpected behavior being introduced into an overriding derived class function that breaks the code that might depend on the base class implementation of that function.
To generally ensure the good use of inheritance in your design you can adopt the following strategy: First, use abstract base classes containing nothing but pure virtual functions to specify the conceptual design of your application at the highest level. This is the approach taken in the fleet simulation application shown in figure 13-12. A pure virtual function has no behavior until it is implemented in a derived class. Thinking in terms of abstract base classes, their interfaces, and their relationships to other abstract base classes is applying inheritance as an aid to conceptual modeling. Implementing abstract base classes and inheriting from them to gain the subtype or “is a...” relationship only is referred to as interface inheritance.
Second, when inheriting from a concrete class such as Person, ensure the derived class is truly a specialization in that it only provides functionality not found in the base class.
Third, avoid, when possible, the urge to override a virtual concrete base class function unless you study the “rules” of doing so as set forth by the designer of the base class code.
| < Day Day Up > |
|