Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
C++ Inheritance
Objectives
• To be able to create new classes by inheriting from
existing classes.
• To understand how inheritance promotes software
reusability.
• To understand the notions of base classes and derived
classes.
Say not you know another entirely, till you have divided an
inheritance with him.
Johann Kasper Lavater
This method is to define as the number of a class the class of
all classes similar to the given class.
Bertrand Russell
A deck of cards was built like the purest of hierarchies, with
every card a master to those below it, a lackey to those above
it.
Ely Culbertson
Good as it is to inherit a library, it is better to collect one.
Augustine Birrell
Save base authority from others’ books.
William Shakespeare, Love’s Labours Lost
Chapter 19 C++ Inheritance 683
Outline
19.1 Introduction
19.2 Inheritance: Base Classes and Derived Classes
19.3 Protected Members
19.4 Casting Base-Class Pointers to Derived-Class Pointers
19.5 Using Member Functions
19.6 Overriding Base-Class Members in a Derived Class
19.7 Public, Protected and Private Inheritance
19.8 Direct Base Classes and Indirect Base Classes
19.9 Using Constructors and Destructors in Derived Classes
19.10 Implicit Derived-Class Object to Base-Class Object Conversion
19.11 Software Engineering with Inheritance
19.12 Composition vs. Inheritance
19.13 “Uses A” and “Knows A” Relationships
19.14 Case Study: Point, Circle, Cylinder
Summary • Terminology • Common Programming Errors • Performance Tips • Software
Engineering Observations • Self-Review Exercises • Answers to Self-Review Exercises •
Exercises
19.1 Introduction
In this and the next chapter we discuss two of the most important capabilities of object-ori-
ented programming—inheritance and polymorphism. Inheritance is a form of software re-
usability in which new classes are created from existing classes by absorbing their attributes
and behaviors and overriding or embellishing these with capabilities the new classes require.
Software reusability saves time in program development. It encourages the reuse of proven
and debugged high-quality software, thus reducing problems after a system becomes func-
tional. These are exciting possibilities. Polymorphism enables us to write programs in a gen-
eral fashion to handle a wide variety of existing and yet-to-be-specified related classes.
Inheritance and polymorphism are effective techniques for managing software complexity.
When creating a new class, instead of writing completely new data members and
member functions, the programmer can designate that the new class is to inherit the data
members and member functions of a previously defined base class. The new class is
referred to as a derived class. Each derived class itself becomes a candidate to be a base
class for some future derived class. With single inheritance, a class is derived from one
base class. With multiple inheritance, a derived class inherits from multiple (possibly unre-
lated) base classes. Single inheritance is straightforward—we show several examples that
should enable the reader to become proficient quickly. Multiple inheritance is complex and
error prone—we briefly discuss this advanced topic here and issue a strong caution urging
the reader to pursue further study before using this powerful capability.
684 C++ Inheritance Chapter 19
A derived class can add data members and member functions of its own, so a derived
class can be larger than its base class. A derived class is more specific than its base class
and represents a smaller group of objects. With single inheritance, the derived class starts
out essentially the same as the base class. The real strength of inheritance comes from the
ability to define in the derived class additions, replacements or refinements to the features
inherited from the base class.
C++ offers three kinds of inheritance—public, protected and private. In this
chapter we concentrate on public inheritance and briefly explain the other two kinds. The
second form, private inheritance, can be used as an alternative form of composition. The
third form, protected inheritance, is a relatively recent addition to C++ and is rarely
used. With public inheritance, every object of a derived class may also be treated as an
object of that derived class’ base class. However, the converse is not true—base-class
objects are not objects of that base class’ derived classes. We will take advantage of this
“derived-class-object-is-a-base-class-object” relationship to perform some interesting
manipulations. For example, we can thread a wide variety of different objects related
through inheritance into a linked list of base-class objects. This allows a variety of objects
to be processed in a general way. As we will see in the next chapter, this capability—called
polymorphism—is a key thrust of object-oriented programming.
We add a new form of member access control in this chapter, namely, protected
access. Derived classes and their friends can access protected base-class members,
whereas non-friend, non-derived-class-member functions cannot.
Experience in building software systems indicates that significant portions of the code
deal with closely related special cases. It becomes difficult in such systems to see the “big
picture,” because the designer and the programmer become preoccupied with the special
cases. Object-oriented programming provides several ways of “seeing the forest through
the trees”—a process called abstraction.
If a program is loaded with closely related special cases, then it is common to see
switch statements that distinguish among the special cases and provide the processing
logic to deal with each case individually. In Chapter 20, we show how to use inheritance
and polymorphism to replace such switch logic with simpler logic.
We distinguish between “is a” relationships and “has a” relationships. “Is a” is inher-
itance. In an “is a” relationship, an object of a derived-class type may also be treated as an
object of the base-class type. “Has a” is composition (see Fig. 17.4). In a “has a” relation-
ship, a class object has one or more objects of other classes as members.
A derived class cannot access the private members of its base class; allowing this
would violate the encapsulation of the base class. A derived class can, however, access the
public and protected members of its base class. Base-class members that should not
be accessible to a derived class via inheritance are declared private in the base class. A
derived class can access private members of the base class only through access func-
tions provided in the base class’ public and protected interfaces.
One problem with inheritance is that a derived class can inherit public member func-
tion implementations that it does not need to have or should expressly not have. When a
base-class member implementation is inappropriate for a derived class, that member can be
overridden in the derived class with an appropriate implementation. In some cases,
public inheritance is simply inappropriate.
Chapter 19 C++ Inheritance 685
Perhaps most exciting is the notion that new classes can inherit from existing class
libraries. Organizations develop their own class libraries and can take advantage of other
libraries available worldwide. Eventually, software will be constructed predominantly
from standardized reusable components just as hardware is often constructed today. This
will help to meet the challenges of developing the ever more powerful software we will
need in the future.
Student GraduateStudent
UndergraduateStudent
Shape Circle
Triangle
Rectangle
Loan CarLoan
HomeImprovementLoan
MortgageLoan
Employee FacultyMember
StaffMember
Account CheckingAccount
SavingsAccount
CommunityMember
Shape
TwoDimensionalShape ThreeDimensionalShape
This is called public inheritance and is the most commonly used type of inheritance. We
will also discuss private inheritance and protected inheritance. With public in-
heritance, the public and protected base class members are inherited as public
and protected members of the derived class, respectively. Remember that private
members of a base class are not accessible from that class’ derived classes. Note that
friend functions are not inherited.
It is possible to treat base-class objects and derived-class objects similarly; that com-
monality is expressed in the attributes and behaviors of the base class. Objects of any class
derived with public inheritance from a common base class can all be treated as objects
of that base class. We will consider many examples in which we can take advantage of this
relationship with an ease of programming not available in non-object-oriented languages,
such as C.
The programmer may, however, use an explicit cast to convert a base-class pointer to
a derived-class pointer. This process is often called downcasting a pointer. But be careful—
if such a pointer is to be dereferenced, then the programmer should be sure that the type of
the pointer matches the type of the object to which it points. Our treatment in this section
uses techniques widely available in most compilers.
688 C++ Inheritance Chapter 19
Our first example is shown in Fig. 19.4. Lines 1–43 show the Point class definition
and Point member function definitions. Lines 44–106 show the Circle class definition
and Circle member function definitions. Lines 107–147 show a driver program in which
we demonstrate assigning derived-class pointers to base-class pointers (often called
upcasting a pointer) and casting base-class pointers to derived-class pointers.
36
37 // Output Point (with overloaded stream insertion operator)
38 ostream &operator<<( ostream &output, const Point &p )
39 {
40 output << '[' << p.x << ", " << p.y << ']';
41
42 return output; // enables cascaded calls
43 }
126 // Treat a Circle as a Point (see only the base class part)
127 pointPtr = &c; // assign address of Circle to pointPtr
128 cout << "\nCircle c (via *pointPtr): "
129 << *pointPtr << '\n';
130
131 // Treat a Circle as a Circle (with some casting)
132 // cast base-class pointer to derived-class pointer
133 circlePtr = static_cast< Circle * >( pointPtr );
134 cout << "\nCircle c (via *circlePtr):\n" << *circlePtr
135 << "\nArea of c (via circlePtr): "
136 << circlePtr->area() << '\n';
137
138 // DANGEROUS: Treat a Point as a Circle
139 pointPtr = &p; // assign address of Point to pointPtr
140
141 // cast base-class pointer to derived-class pointer
142 circlePtr = static_cast< Circle * >( pointPtr );
143 cout << "\nPoint p (via *circlePtr):\n" << *circlePtr
144 << "\nArea of object circlePtr points to: "
145 << circlePtr->area() << endl;
146 return 0;
147 }
Let us examine the Point class definition. The public interface to Point includes
member functions setPoint, getX and getY. The data members x and y of Point are
specified as protected. This prevents clients of Point objects from directly accessing the
data, but enables classes derived from Point to access the inherited data members directly.
If the data were private, the public member functions of Point would be used to
access the data, even by derived classes. Note that the Point overloaded stream-insertion
operator function is able to reference variables x and y directly because the stream-insertion
operator function is a friend of class Point. Note also that it is necessary to reference x
and y through objects as in p.x and p.y. This is because the stream-insertion operator func-
tion is not a member function of the class Point, so we must use an explicit handle so the
compiler knows what object we are referencing. Note that this class offers inlined
public member functions getX and getY, so operator<< does not need to be a
friend to achieve good performance. However, needed public member functions may
not be provided in the public interface of every class, so friendship is often appropriate.
692 C++ Inheritance Chapter 19
Class Circle inherits from class Point with public inheritance. This is specified
in the first line of the class definition:
class Circle : public Point { // Circle inherits from Point
The colon (:) in the header of the class definition indicates inheritance. The keyword pub-
lic indicates the type of inheritance. (In Section 19.7 we will discuss protected and
private inheritance.) All the public and protected members of class Point are
inherited as public and protected members, respectively, into class Circle. This
means that the public interface to Circle includes the Point public members as
well as the Circle public members area, setRadius and getRadius.
The Circle constructor must invoke the Point constructor to initialize the Point
base-class portion of a Circle object. This is accomplished with a member initializer
(introduced in Chapter 17) as follows:
Circle::Circle( double r, int a, int b )
: Point( a, b ) // call base-class constructor
The second line of the constructor header invokes the Point constructor by name. Values
a and b are passed from the Circle constructor to the Point constructor to initialize the
base-class members x and y. If the Circle constructor did not invoke the Point con-
structor explicitly, the default Point constructor would be invoked implicitly with the de-
fault values for x and y (i.e., 0 and 0). If in this case the Point class did not provide a
default constructor, the compiler would issue a syntax error. Note that the Circle over-
loaded operator<< function is able to output the Point part of the Circle by casting
the Circle reference c to a Point. This results in a call to operator<< for Point and
outputs the x and y coordinates using the proper Point formatting.
The driver program creates pointPtr as a pointer to a Point object and instantiates
Point object p, then creates circlePtr as a pointer to a Circle object and instanti-
ates Circle object c. The objects p and c are output using their overloaded stream-inser-
tion operators to show that they were initialized correctly. Next, the driver assigns a
derived-class pointer (the address of object c) to base-class pointer pointPtr and outputs
the Circle object c using operator<< for Point and the dereferenced pointer
*pointPtr. Note that only the Point portion of the Circle object c is displayed.
With public inheritance, it is always valid to assign a derived-class pointer to a base-
class pointer because a derived-class object is a base-class object. The base-class pointer
“sees” only the base-class part of the derived-class object. The compiler performs an
implicit conversion of the derived-class pointer to a base-class pointer.
Then, the driver program demonstrates casting pointPtr back to a Circle *. The
result of the cast operation is assigned to circlePtr. The Circle object c is output
using the overloaded stream-insertion operator for Circle and the dereferenced pointer
*circlePtr. The area of Circle object c is output via circlePtr. This results in a
valid area value because the pointers are always pointing to a Circle object.
A base-class pointer cannot be assigned directly to a derived-class pointer, because this
is an inherently dangerous assignment—derived-class pointers expect to be pointing to
derived-class objects. The compiler does not perform an implicit conversion in this case.
Using an explicit cast informs the compiler that the programmer knows this type of pointer
conversion is dangerous—the programmer assumes responsibility for using the pointer
appropriately, so the compiler is willing to allow the dangerous conversion.
Chapter 19 C++ Inheritance 693
Next, the driver assigns a base-class pointer (the address of object p) to base-class
pointer pointPtr and casts pointPtr back to a Circle *. The result of the cast oper-
ation is assigned to circlePtr. Point object p is output using operator<< for
Circle and the dereferenced pointer *circlePtr. Note the zero value output for the
radius member (which actually does not exist, because circlePtr is really aimed at a
Point object). Outputting a Point as a Circle results in an undefined value (in this
case it happens to be zero) for the radius, because the pointers are always pointing to a
Point object. A Point object does not have a radius member. Therefore, the program
outputs whatever value happens to be in memory at the location that circlePtr expects
the radius data member to be. The area of the object pointed to by circlePtr (Point
object p) is also output via circlePtr. Note that the value for the area is 0.00 because
this calculation is based on the “undefined” value of the radius. Obviously, accessing
data members that are not there is dangerous. Calling member functions that do not exist
can crash a program.
In this section we have shown the mechanics of pointer conversions. This material
establishes the foundation we will need for our deeper treatment of object-oriented pro-
gramming with polymorphism in the next chapter.
This is a crucial aspect of software engineering in C++. If a derived class could access
the base class’ private members, this would violate the encapsulation of the base class.
Hiding private members is a huge help in testing, debugging and correctly modifying
systems. If a derived class could access its base class’ private members, it would then
be possible for classes derived from that derived class to access those data as well, and so
on. This would propagate access to what is supposed to be private data, and the benefits
of encapsulation would be lost throughout the class hierarchy.
26
27 // Constructor dynamically allocates space for the
28 // first and last name and uses strcpy to copy
29 // the first and last names into the object.
30 Employee::Employee( const char *first, const char *last )
31 {
32 firstName = new char[ strlen( first ) + 1 ];
33 assert( firstName != 0 ); // terminate if not allocated
34 strcpy( firstName, first );
35
36 lastName = new char[ strlen( last ) + 1 ];
37 assert( lastName != 0 ); // terminate if not allocated
38 strcpy( lastName, last );
39 }
40
41 // Output employee name
42 void Employee::print() const
43 { cout << firstName << ' ' << lastName; }
44
45 // Destructor deallocates dynamically allocated memory
46 Employee::~Employee()
47 {
48 delete [] firstName; // reclaim dynamic memory
49 delete [] lastName; // reclaim dynamic memory
50 }
115 h.print();
116 return 0;
117 }
HourlyWorker::print() is executing
Bob Smith is an hourly worker with pay of $400.00
The Employee class definition consists of two private char * data members—
firstName and lastName—and three member functions—a constructor, a destructor
and print. The constructor function receives two strings and dynamically allocates char-
acter arrays to store the strings. Note that the assert macro is used to determine if
memory was allocated to firstName and lastName. If not, the program terminates
with an error message indicating the condition tested, the line number on which the con-
dition appears and the file in which the condition is located. [Note, once again, that in the
C++ standard, new “throws” an exception if insufficient memory is available; we discuss
this in Chapter 23.] Because the data of Employee are private, the only access to the
data is through member function print which simply outputs the first name and last name
of the employee. The destructor function returns the dynamically allocated memory to the
system (to avoid a “memory leak”).
Class HourlyWorker inherits from class Employee with public inheritance.
Again, this is specified in the first line of the class definition using the colon (:) notation
as follows:
class HourlyWorker : public Employee
The public interface to HourlyWorker includes the Employee print function and
HourlyWorker member functions getPay and print. Note that HourlyWorker de-
fines its own print function with the same prototype as Employee::print()—this is
an example of function overriding. Therefore, class HourlyWorker has access to two
print functions. Class HourlyWorker also contains private data members wage and
hours for calculating the employee’s weekly salary.
The HourlyWorker constructor uses member initializer syntax to pass the strings
first and last to the Employee constructor so the base-class members can be initial-
ized, then initializes members hours and wage. Member function getPay calculates the
salary of the HourlyWorker.
HourlyWorker member function print overrides the Employee print
member function. Often, base-class member functions are overridden in a derived class to
provide more functionality. The overridden functions sometimes call the base-class version
of the function to perform part of the new task. In this example, the derived-class print
function calls the base-class print function to output the employee’s name (the base-class
print is the only function with access to the private data of the base class). The
derived-class print function also outputs the employee’s pay. Note how the base-class
version of print is called
Employee::print();
698 C++ Inheritance Chapter 19
Because the base-class function and the derived-class function have the same name and sig-
nature, the base-class function must be preceded by its class name and the scope resolution
operator. Otherwise, the derived-class version of the function would be called causing in-
finite recursion (i.e., the HourlyWorker print function would call itself).
When deriving from a protected base class, public and protected members
of the base class become protected members of the derived class. When deriving from
a private base class, public and protected members of the base class become
private members (e.g., the functions become utility functions) of the derived class.
Private and protected inheritance are not “is a” relationships.
Figure 19.7 demonstrates the order in which base-class and derived-class constructors
and destructors are called. Lines 1 through 39 show a simple Point class containing a con-
structor, a destructor and protected data members x and y. The constructor and
destructor both print the Point object for which they are invoked.
Fig. 19.7 Order in which base-class and derived-class constructors and destructors
are called—point2.h .
Fig. 19.7 Order in which base-class and derived-class constructors and destructors
are called—point2.cpp .
Chapter 19 C++ Inheritance 701
Fig. 19.7 Order in which base-class and derived-class constructors and destructors
are called—circle2.h.
Fig. 19.7 Order in which base-class and derived-class constructors and destructors
are called—circle2.cpp .
702 C++ Inheritance Chapter 19
Fig. 19.7 Order in which base-class and derived-class constructors and destructors
are called—fig19_07.cpp .
Lines 40 through 81 show a simple Circle class derived from Point with public
inheritance. Class Circle provides a constructor, a destructor and a private data
member radius. The constructor and destructor both print the Circle object for which
they are invoked. The Circle constructor also invokes the Point constructor using
member initializer syntax and passes the values a and b so the base-class data members x
and y can be initialized.
Chapter 19 C++ Inheritance 703
Lines 82 through 106 are the driver program for this Point/Circle hierarchy. The
program begins by instantiating a Point object in a scope inside main. The object goes
in and out of scope immediately, so the Point constructor and destructor are both called.
Next, the program instantiates Circle object circle1. This invokes the Point con-
structor to perform output with values passed from the Circle constructor, then performs
the output specified in the Circle constructor. Circle object circle2 is instantiated
next. Again, the Point and Circle constructors are both called. Note that the body of
the Point constructor is performed before the body of the Circle constructor. The end
of main is reached, so the destructors are called for objects circle1 and circle2.
Destructors are called in the reverse order of their corresponding constructors. Therefore,
the Circle destructor and Point destructor are called in that order for object circle2,
then the Circle and Point destructors are called in that order for object circle1.
A base class specifies commonality—all classes derived from a base class inherit the
capabilities of that base class. In the object-oriented design process, the designer looks for
commonality and “factors it out” to form desirable base classes. Derived classes are then
customized beyond the capabilities inherited from the base class.
Software Engineering Observation 19.8
In an object-oriented system, classes are often closely related. “Factor out” common attributes
and behaviors and place these in a base class. Then use inheritance to form derived classes.
Just as the designer of non-object-oriented systems seeks to avoid unnecessary prolifer-
ation of functions, the designer of object-oriented systems should avoid unnecessary prolif-
eration of classes. Such a proliferation of classes creates management problems and can
hinder software reusability simply because it is more difficult for a potential reuser of a class
to locate that class in a huge collection. The trade-off is to create fewer classes, each providing
substantial additional functionality. Such classes might be too rich for certain reusers; they
can mask the excessive functionality, thus “toning down” the classes to meet their needs.
Performance Tip 19.2
If classes produced through inheritance are larger than they need to be, memory and pro-
cessing resources may be wasted. Inherit from the class “closest” to what you need.
Note that reading a set of derived-class declarations can be confusing because inherited
members are not shown, but they are nevertheless present in the derived classes. A similar
problem can exist in the documentation of derived classes.
Software Engineering Observation 19.9
A derived class contains the attributes and behaviors of its base class. A derived class can
also contain additional attributes and behaviors. With inheritance, the base class can be
compiled independently of the derived class. Only the derived class' incremental attributes
and behaviors need to be compiled to be able to combine these with the base class to form
the derived class.
Software Engineering Observation 19.10
Modifications to a base class do not require derived classes to change as long as the pub-
lic and protected interfaces to the base class remain unchanged. Derived classes may,
however, need to be recompiled.
X coordinate is 72
Y coordinate is 115
Our next example is shown in Fig. 19.9. The Point class definition and the member
function definitions from Fig. 19.8 are reused here. Lines 1 through 62 show the Circle
class definition and Circle member function definitions. Lines 63 through 90 are the
driver program for class Circle. Note that class Circle inherits from class Point with
public inheritance. This means that the public interface to Circle includes the
Point member functions as well as the Circle member functions setRadius,
getRadius and area.
40
41 // Set radius
42 void Circle::setRadius( double r )
43 { radius = ( r >= 0 ? r : 0 ); }
44
45 // Get radius
46 double Circle::getRadius() const { return radius; }
47
48 // Calculate area of Circle
49 double Circle::area() const
50 { return 3.14159 * radius * radius; }
51
52 // Output a circle in the form:
53 // Center = [x, y]; Radius = #.##
54 ostream &operator<<( ostream &output, const Circle &c )
55 {
56 output << "Center = " << static_cast< Point > ( c )
57 << "; Radius = "
58 << setiosflags( ios::fixed | ios::showpoint )
59 << setprecision( 2 ) << c.radius;
60
61 return output; // enables cascaded calls
62 }
86 Point &pRef = c;
87 cout << "\nCircle printed as a Point is: " << pRef << endl;
88
89 return 0;
90 }
X coordinate is 37
Y coordinate is 43
Radius is 2.5
The driver program instantiates an object of class Cylinder then uses get functions
to obtain the information about the Cylinder object. Again, main is neither a member
function nor a friend of class Cylinder, so it cannot directly reference the pro-
tected data of class Cylinder. The driver program then uses set functions set-
Height, setRadius and setPoint to reset the height, radius and coordinates of the
cylinder. Finally, the driver initializes reference variable pRef of type “reference to
Point object” (Point &) to Cylinder object cyl. It then prints pRef, which, despite
the fact that it is initialized with a Cylinder object, “thinks” it is a Point object, so the
Cylinder object actually prints as a Point object. The driver then initializes reference
variable circleRef of type “reference to Circle object” (Circle &) to Cylinder
object cyl. The driver program then prints circleRef, which, despite the fact that it is
initialized with a Cylinder object, “thinks” it is a Circle object, so the Cylinder
object actually prints as a Circle object. The area of the Circle is also output.
This example nicely demonstrates public inheritance and defining and referencing
protected data members. The reader should now be confident with the basics of inher-
itance. In the next chapter, we show how to program with inheritance hierarchies in a gen-
eral manner using polymorphism. Data abstraction, inheritance and polymorphism are the
crux of object-oriented programming.
X coordinate is 12
Y coordinate is 23
Radius is 2.5
Height is 5.7
SUMMARY
• One of the keys to the power of object-oriented programming is achieving software reusability
through inheritance.
714 C++ Inheritance Chapter 19
• The programmer can designate that the new class is to inherit the data members and member func-
tions of a previously defined base class. In this case, the new class is referred to as a derived class.
• With single inheritance, a class is derived from only one base class. With multiple inheritance, a
derived class inherits from multiple (possibly unrelated) base classes.
• A derived class normally adds data members and member functions of its own, so a derived class
generally has a larger definition than its base class. A derived class is more specific than its base
class and normally represents fewer objects.
• A derived class cannot access the private members of its base class; allowing this would violate
the encapsulation of the base class. A derived class can, however, access the public and pro-
tected members of its base class.
• A derived-class constructor always calls the constructor for its base class first to create and initial-
ize the derived class’ base-class members.
• Destructors are called in the reverse order of constructor calls, so a derived-class destructor is
called before its base-class destructor.
• Inheritance enables software reusability, which saves time in development and encourages the use
of previously proven and debugged high-quality software.
• Inheritance can be accomplished from existing class libraries.
• Someday most software will be constructed from standardized reusable components, as most hard-
ware is constructed today.
• The implementor of a derived class does not need access to the source code of a base class, but
does need the interface to the base class and the base class’ object code.
• An object of a derived class can be treated as an object of its corresponding public base class. How-
ever, the reverse is not true.
• A base class exists in a hierarchical relationship with its singly derived classes.
• A class can exist by itself. When that class is used with the mechanism of inheritance, it becomes
either a base class that supplies attributes and behaviors to other classes, or the class becomes a
derived class that inherits those attributes and behaviors.
• An inheritance hierarchy can be arbitrarily deep within the physical limitations of a particular system.
• Hierarchies are useful tools for understanding and managing complexity. With software becoming
increasingly complex, C++ provides mechanisms for supporting hierarchical structures through
inheritance and polymorphism.
• An explicit cast can be used to convert a base-class pointer to a derived-class pointer. Such a point-
er should not be dereferenced unless it actually points to an object of the derived class type.
• Protected access serves as an intermediate level of protection between public access and
private access. Protected members of a base class may be accessed by members and
friends of the base class and by members and friends of derived classes; no other functions
can access the protected members of a base class.
• Protected members are used to extend privileges to derived classes while denying those priv-
ileges to nonclass, non-friend functions.
• When deriving a class from a base class, the base class may be declared as either public, pro-
tected or private.
• When deriving a class from a public base class, public members of the base class become
public members of the derived class, and protected members of the base class become
protected members of the derived class.
Chapter 19 C++ Inheritance 715
• When deriving a class from a protected base class, public and protected members of the
base class become protected members of the derived class.
• When deriving a class from a private base class, public and protected members of the
base class become private members of the derived class.
• A base class may be either a direct base class of a derived class or an indirect base class of a derived
class. A direct base class is explicitly listed where the derived class is declared. An indirect base
class is not explicitly listed; rather it is inherited from several levels up the class hierarchy tree.
• When a base-class member is inappropriate for a derived class, we may simply redefine that mem-
ber in the derived class.
• It is important to distinguish between “is a” relationships and “has a” relationships. In a “has a”
relationship, a class object has an object of another class as a member. In an “is a” relationship, an
object of a derived-class type may also be treated as an object of the base-class type. “Is a” is in-
heritance. “Has a” is composition.
• A derived-class object can be assigned to a base-class object. This kind of assignment makes sense
because the derived class has members corresponding to each of the base-class members.
• A pointer to a derived-class object can be implicitly converted into a pointer for a base-class object.
• It is possible to convert a base-class pointer to a derived-class pointer by using an explicit cast. The
target should be a derived-class object.
• A base class specifies commonality. All classes derived from a base class inherit the capabilities
of that base class. In the object-oriented design process, the designer looks for commonality and
factors it out to form desirable base classes. Derived classes are then customized beyond the capa-
bilities inherited from the base class.
• Reading a set of derived-class declarations can be confusing because not all the members of the
derived class are present in these declarations. In particular, inherited members are not listed in the
derived-class declarations, but these members are indeed present in the derived classes.
• “Has a” relationships are examples of creating new classes by composition of existing classes.
• “Knows a” relationships are examples of objects containing pointers or references to other objects
so they can be aware of those objects.
• Member object constructors are called in the order in which the objects are declared. In inherit-
ance, base-class constructors are called in the order in which inheritance is specified and before
the derived-class constructor.
• For a derived-class object, first the base-class constructor is called, then the derived-class con-
structor is called (which may call member object constructors).
• When the derived-class object is destroyed, the destructors are called in the reverse order of the
constructors—first the derived-class destructor is called, then the base-class destructor is called.
• A class may be derived from more than one base class; such derivation is called multiple inheritance.
• Indicate multiple inheritance by following the colon (:) inheritance indicator with a comma-sep-
arated list of base classes.
• The derived-class constructor calls base-class constructors for each of its base classes through the
member-initializer syntax. Base-class constructors are called in the order in which the base classes
are declared during inheritance.
TERMINOLOGY
abstraction base class
ambiguity in multiple inheritance base-class constructor
association base class default constructor
716 C++ Inheritance Chapter 19
PERFORMANCE TIPS
19.1 When performance is a major concern, programmers may want to see source code of classes
they are inheriting from so they can tune the code to meet their performance requirements.
19.2 If classes produced through inheritance are larger than they need to be, memory and process-
ing resources may be wasted. Inherit from the class “closest” to what you need.
19.2 A derived class cannot directly access private members of its base class.
19.3 Suppose we create an object of a derived class where both the base class and the derived class
contain objects of other classes. When an object of that derived class is created, first the con-
structors for the base class’ member objects execute, then the base-class constructor executes,
then the constructors for the derived class’ member objects execute, then the derived class’
constructor executes. Destructors are called in the reverse of the order in which their corre-
sponding constructors are called.
19.4 The order in which member objects are constructed is the order in which those objects are
declared within the class definition. The order in which the member initializers are listed does
not affect the order of construction.
19.5 In inheritance, base-class constructors are called in the order in which inheritance is specified
in the derived-class definition. The order in which the base-class constructors are specified in
the derived-class member initializer list does not affect the order of construction.
19.6 In theory, users do not need to see the source code of classes from which they inherit. In prac-
tice, people who license classes tell us that the customers often demand the source code. Pro-
grammers still seem reluctant to incorporate code into their programs when this code has
been written by other people.
19.7 Creating a derived class does not affect its base class' source code or object code; the integrity
of a base class is preserved by inheritance.
19.8 In an object-oriented system, classes are often closely related. “Factor out” common at-
tributes and behaviors and place these in a base class. Then use inheritance to form derived
classes.
19.9 A derived class contains the attributes and behaviors of its base class. A derived class can also
contain additional attributes and behaviors. With inheritance, the base class can be compiled
independently of the derived class. Only the derived class' incremental attributes and behav-
iors need to be compiled to be able to combine these with the base class to form the derived
class.
19.10 Modifications to a base class do not require derived classes to change as long as the public
and protected interfaces to the base class remain unchanged. Derived classes may, how-
ever, need to be recompiled.
19.11 Program modifications to a class that is a member of another class do not require the enclos-
ing class to change as long as the public interface to the member class remains unchanged.
Note that the composite class may, however, need to be recompiled.
SELF-REVIEW EXERCISES
19.1 Fill in the blanks in each of the following:
a) If the class Alpha inherits from the class Beta, class Alpha is called the
class and class Beta is called the class.
b) C++ provides for , which allows a derived class to inherit from many base
classes, even if these base classes are unrelated.
c) Inheritance enables , which saves time in development and encourages using
previously proven and high-quality software.
d) An object of a class can be treated as an object of its corresponding
class.
e) To convert a base-class pointer to a derived-class pointer, a must be used be-
cause the compiler considers this a dangerous operation.
f) The three member access specifiers are , and .
718 C++ Inheritance Chapter 19
g) When deriving a class from a base class with public inheritance, public members of
the base class become members of the derived class, and protected
members of the base class become members of the derived class.
h) When deriving a class from a base class with protected inheritance, public base
class members become members of the derived class and protected base
class members become members of the derived class.
i) A “has a” relationship between classes represents and an “is a” relationship
between classes represents .
EXERCISES
19.2 Consider the class Bicycle. Given your knowledge of some common components of bicy-
cles, show a class hierarchy in which the class Bicycle inherits from other classes, which, in turn,
inherit from yet other classes. Discuss the instantiation of various objects of class Bicycle. Discuss
inheritance from class Bicycle for other closely related derived classes.
19.3 Briefly define each of the following terms: inheritance, multiple inheritance, base class and
derived class.
19.4 Discuss why converting a base-class pointer to a derived-class pointer is considered dan-
gerous by the compiler.
19.5 (True/False) A derived class is often called a subclass because it represents a subset of its
base class (i.e., a derived class is generally smaller than its base class).
19.6 (True/False) A derived-class object is also an object of that derived class’ base class.
19.7 Some programmers prefer not to use protected access because it breaks the encapsulation
of the base class. Discuss the relative merits of using protected access vs. insisting on using
private access in base classes.
19.8 Many programs written with inheritance could be solved with composition instead, and vice
versa. Discuss the relative merits of these approaches in the context of the Point, Circle, Cyl-
inder class hierarchy in this chapter. Rewrite the program of Fig. 19.10 (and the supporting classes)
to use composition rather than inheritance. After you do this, reassess the relative merits of the two
approaches both for the Point, Circle, Cylinder problem and for object-oriented programs in
general.
19.9 In the chapter, we stated, “When a base-class member is inappropriate for a derived class,
that member can be overridden in the derived class with an appropriate implementation.” If this is
done, does the derived-class-is-a-base-class-object relationship still hold? Explain your answer.
19.10 Study the inheritance hierarchy of Fig. 19.2. For each class, indicate some common attributes
and behaviors consistent with the hierarchy. Add some other classes (UndergraduateStudent,
GraduateStudent, Freshman, Sophomore, Junior, Senior, etc.) to enrich the hierarchy.
19.11 Write an inheritance hierarchy for class Quadrilateral, Trapezoid, Parallelo-
gram, Rectangle and Square. Use Quadrilateral as the base class of the hierarchy. Make
the hierarchy as deep (i.e., as many levels) as possible. The private data of Quadrilateral
should be the (x, y) coordinate pairs for the four endpoints of the Quadrilateral. Write a driver
program that instantiates and displays objects of each of these classes.
Chapter 19 C++ Inheritance 719
19.12 Write down all the shapes you can think of—both two-dimensional and three-dimensional—
and form those shapes into a shape hierarchy. Your hierarchy should have base class Shape from
which class TwoDimensionalShape and class ThreeDimensionalShape are derived. Once
you have developed the hierarchy, define each of the classes in the hierarchy. We will use this hier-
archy in the exercises of Chapter 20 to process all shapes as objects of base-class Shape. This is a
technique called polymorphism.