cpphtp4 09
cpphtp4 09
Object-Oriented
Programming:
Inheritance
Objectives
To be able to create classes by inheriting from existing
classes.
To understand how inheritance promotes software
reusability.
To understand the notions of base classes and derived
classes.
To understand the protected member-access
modifier.
To understand the use of constructors and destructors
in inheritance hierarchies.
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
610 Object-Oriented Programming: Inheritance Chapter 9
Outline
9.1 Introduction
9.2 Base Classes and Derived Classes
9.3 protected Members
9.4 Relationship between Base Classes and Derived Classes
9.5 Case Study: Three-Level Inheritance Hierarchy
9.6 Constructors and Destructors in Derived Classes
9.7 Uses A and Knows A Relationships
9.8 public, protected and private Inheritance
9.9 Software Engineering with Inheritance
9.10 [Optional Case Study] Thinking About Objects: Incorporating
Inheritance into the Elevator Simulation
Summary Terminology Self-Review Exercises Answers to Self-Review Exercises Exercises
9.1 Introduction
In this chapter, we begin our discussion of object-oriented programming (OOP) by intro-
ducing one of its main featuresinheritance. Inheritance is a form of software reusability
in which programmers create classes that absorb an existing classs data and behaviors and
enhance them with new capabilities. Software reusability saves time during program devel-
opment. It also encourages the reuse of proven and debugged high-quality software, which
increases the likelihood that a system will be implemented effectively.
When creating a class, instead of writing completely new data members and member
functions, the programmer can designate that the new class should inherit the members of
an existing class. This existing class is called the base class, and the new class is referred
to as the derived class. (Other programming languages, such as Java, refer to the base
class as the superclass and the derived class as the subclass.) A derived class represents a
more specialized group of objects. Typically, a derived class contains behaviors inherited
from its base class plus additional behaviors. As we will see, a derived class can also cus-
tomize behaviors inherited from the base class. A direct base class is the base class from
which a derived class explicitly inherits. An indirect base class is inherited from two or
more levels up the class hierarchy. In the case of single inheritance, a class is derived from
one base class. C++ also supports multiple inheritance, in which a derived class inherits
from multiple (possibly unrelated) base classes. Single inheritance is straightforwardwe
show several examples that should enable the reader to become proficient quickly. Multiple
inheritance can be complex and error prone. We cover multiple inheritance in Chapter 22.
C++ offers three kinds of inheritancepublic, protected and private. In this
chapter, we concentrate on public inheritance and briefly explain the other two kinds. In
Chapter 17, we show how private inheritance can be used as an alternative to composi-
tion. The third form, protected inheritance, is rarely used. With public inheritance,
every object of a derived class is also an object of that derived classs base class. However,
base-class objects are not objects of their derived classes. For example, all cars are vehicles,
Chapter 9 Object-Oriented Programming: Inheritance 611
but not all vehicles are cars. As we continue our study of object-oriented programming in
Chapter 9 and Chapter 10, we take advantage of this relationship to perform some inter-
esting manipulations.
Experience in building software systems indicates that significant portions of code
deal with closely related special cases. When programmers are preoccupied with special
cases, the details can obscure the big picture. With object-oriented programming, pro-
grammers focus on the commonalities among objects in the system, rather than on the spe-
cial cases. This process is called abstraction.
We distinguish between the is-a relationship and the has-a relationship. The is-
a relationship represents inheritance. In an is-a relationship, an object of a derived class
also can be treated as an object of its base classfor example, a car is a vehicle, so any
properties and behaviors of a vechicle are also properties of a car. By contrast, the has-a
relationship stands for composition. (Composition was discussed in Chapter 7.) In a has-
a relationship, an object contains one or more objects of other classes as membersfor
example, a car has a steering wheel.
Derived-class member functions might require access to base-class data members and
member functions. A derived class can access the non-private members of its base class.
Base-class members that should not be accessible to the member functions of derived
classes should be declared private in the base class. A derived class can effect state
changes in private base-class members, but only through non-private member func-
tions provided in the base class and inherited into the derived class.
Software Engineering Observation 9.1
Member functions of a derived class cannot directly access private members of their
classs base class. 9.1
One problem with inheritance is that a derived class can inherit data members and
member functions it does not need or should not have. It is the class designers responsi-
bility to ensure that the capabilities provided by a class are appropriate for future derived
classes. Even when a base-class member function is appropriate for a derived class, the
derived class often requires that member function to behave in a manner specific to the
derived class. In such cases, the base-class member function can be redefined in the derived
class with an appropriate implementation.
Student GraduateStudent
UndergraduateStudent
Shape Circle
Triangle
Rectangle
Loan CarLoan
HomeImprovementLoan
MortgageLoan
Employee Faculty
Staff
Account CheckingAccount
SavingsAccount
Because every derived-class object is an object of its base class, and one base class
can have many derived classes, the set of objects represented by a base class typically is
larger than the set of objects represented by any of its derived classes. For example, the base
class Vehicle represents all vehicles, including cars, trucks, boats, bicycles and so on. By
contrast, derived class Car represents a smaller, more-specific subset of all vehicles.
Inheritance relationships form tree-like hierarchical structures. A base class exists in a
hierarchical relationship with its derived classes. Although classes can exist independently,
once they are employed in inheritance relationships, they become affiliated with other
classes. A class becomes either a base class, supplying data and behaviors to other classes,
or a derived class, inheriting its data and behaviors from other classes.
Let us develop a simple inheritance hierarchy. A university community has thousands
of members. These members consist of employees, students and alumni. Employees are
either faculty members or staff members. Faculty members are either administrators (such
as deans and department chairpersons) or teachers. This organizational structure yields the
inheritance hierarchy depicted in Fig. 9.2. Note that this inheritance hierarchy could con-
tain many other classes. For example, students can be graduate or undergraduate students.
Undergraduate students can be freshmen, sophomores, juniors and seniors. Each arrow in
the hierarchy represents an is-a relationship. For example, as we follow the arrows in this
class hierarchy, we can state an Employee is a CommunityMember and a Teacher
is a Faculty member. CommunityMember is the direct base class of Employee,
Student and Alumnus. In addition, CommunityMember is an indirect base class of
all the other classes in the diagram. Starting from the bottom of the diagram, the reader can
follow the arrows and apply the is-a relationship to the topmost base class. For example, an
Administrator is a Faculty member, is an Employee and is a Community-
Member. Note that some administrators also teach classes, so we have used multiple inher-
itance to form class AdministratorTeacher.
Chapter 9 Object-Oriented Programming: Inheritance 613
CommunityMember
Single
Employee Student Alumnus
inheritance
Single
Faculty Staff
inheritance
Single
Administrator Teacher
inheritance
Multiple
AdministratorTeacher
inheritance
Another inheritance hierarchy is the Shape hierarchy of Fig. 9.3. To specify that class
TwoDimensionalShape is derived from (or inherits from) class Shape, class
TwoDimensionalShape could be defined in C++ as follows:
This is an example of public inheritance and is the most commonly used type of inher-
itance. We also will discuss private inheritance and protected inheritance
(Section 9.8). With public inheritance, private members of a base class are not acces-
sible directly from that classs derived classes, but these private base-class members are
still inherited. All other base-class members retain their original member access when they
become members of the derived class (e.g., public members of the base class become
public members of the derived class, and, as we will soon see, protected members
of the base class become protected members of the derived class). Through these in-
herited base-class members, the derived class can manipulate private members of the
base class (if these inherited members provide such functionality in the base class). Note
that friend functions are not inherited.
Inheritance is not appropriate for every class relationship. In Chapter 7, we discussed
the has-a relationship, in which classes have members that are objects of other classes.
Such relationships create classes by composition of existing classes. For example, given the
classes Employee, BirthDate and TelephoneNumber, it is improper to say that an
Employee is a BirthDate or that an Employee is a TelephoneNumber. However,
it is appropriate to say that an Employee has a BirthDate and that an Employee has
a TelephoneNumber.
It is possible to treat base-class objects and derived-class objects similarly; their com-
monalities are expressed in the members of the base class. Objects of all classes derived
from a common base class can be treated as objects of that base class (i.e., such objects have
614 Object-Oriented Programming: Inheritance Chapter 9
Shape
TwoDimensionalShape ThreeDimensionalShape
an is-a relationship with the base class). In Chapter 10, Object-Oriented Programming:
Polymorphism, we consider many examples that take advantage of this relationship.
1. The point/circle relationship may seem unnatural when we say that a circle is a point. This ex-
ample teaches what is sometimes called structural inheritance and focuses on the mechanics of
inheritance and how a base class and a derived class relate to one another. In the exercises and in
Chapter 10, we present more natural inheritance examples.
Chapter 9 Object-Oriented Programming: Inheritance 615
do not use inheritance to create class Circle; rather, we construct the class by writing ev-
ery line of code the class requires. Next, we create a separate Circle2 class, which inher-
its directly from class Point (i.e., class Circle2 is a Point but also contains a
radius) and attempts to access class Points private membersthis results in compi-
lation errors, because the derived class does not have access to the base classs private
data. We then show that if Points data is declared as protected, a Circle3 class
that inherits from class Point2 can access that data. For this purpose, we define class
Point2 with protected data. Both the inherited and noninherited Circle classes
contain identical functionality, but we show how the inherited Circle3 class is easier to
create and manage. After discussing the convenience of using protected data, we set the
Point data back to private in class Point3 (to enforce good software engineering),
then show how a separate Circle4 class (which inherits from class Point3) can use
Point3 member functions to manipulate Point3s private data.
ing data members as private and providing non-private member functions to manip-
ulate and validate the data members enforces good software engineering. [Note: The
Point constructor definition purposely does not use member-initializer syntax in the first
several examples of this section, so that we can demonstrate how private and pro-
tected specifiers affect member access in derived classes. As shown in Fig. 9.5, lines 12
13, we assign values to the data members in the constructor body. Later in this section, we
will return to using member-initializer lists in the constructors.]
Figure 9.6 tests class Point. Line 12 instantiates object point of class Point and
passes 72 as the x-coordinate value and 115 as the y-coordinate value to the constructor.
Lines 1516 use points getX and getY member functions to retrieve these values, then
output the values. Lines 1819 invoke points member functions setX and setY to
change the values for points x and y data members. Line 23 then calls points print
member function to display the new x- and y-coordinate values.
X coordinate is 72
Y coordinate is 115
Fig. 9.8 Circle class contains an xy coordinate and a radius. (Part 1 of 2.)
620 Object-Oriented Programming: Inheritance Chapter 9
53 // return radius
54 double Circle::getRadius() const
55 {
56 return radius;
57
58 } // end function getRadius
59
60 // calculate and return diameter
61 double Circle::getDiameter() const
62 {
63 return 2 * radius;
64
65 } // end function getDiameter
66
67 // calculate and return circumference
68 double Circle::getCircumference() const
69 {
70 return 3.14159 * getDiameter();
71
72 } // end function getCircumference
73
74 // calculate and return area
75 double Circle::getArea() const
76 {
77 return 3.14159 * radius * radius;
78
79 } // end function getArea
80
81 // output Circle object
82 void Circle::print() const
83 {
84 cout << "Center = [" << x << ", " << y << ']'
85 << "; Radius = " << radius;
86
87 } // end function print
Fig. 9.8 Circle class contains an xy coordinate and a radius. (Part 2 of 2.)
to retrieve circles values, then display. Lines 2426 invoke circles setX, setY
and setRadius member functions to change the xy coordinates and the radius, respec-
tively. Member function setRadius (Fig. 9.8, lines 4751) ensures that data member
radius cannot be assigned a negative value (i.e., a circle cannot have a negative radius).
Line 30 of Fig. 9.9 calls circles print member function to display its x-coordinate, y-
coordinate and radius. Lines 3642 call circles getDiameter, getCircumfer-
ence and getArea member functions to display circles diameter, circumference and
area, respectively.
For class Circle (Fig. 9.7Fig. 9.8), note that much of the code is similar, if not iden-
tical, to the code in class Point (Fig. 9.4Fig. 9.5). For example, the declaration in class
Circle of private data members x and y and member functions setX, getX, setY
and getY are identical to those of class Point. In addition, the Circle constructor and
member function print are almost identical to those of class Point, except that they also
Chapter 9 Object-Oriented Programming: Inheritance 621
manipulate the radius. The other additions to class Circle are private data member
radius and member functions setRadius, getRadius, getDiameter, get-
Circumference and getArea.
X coordinate is 37
Y coordinate is 43
Radius is 2.5
The new location and radius of circle are
Center = [2, 2]; Radius = 4.25
Diameter is 8.50
Circumference is 26.70
Area is 56.74
14
15 void setRadius( double ); // set radius
16 double getRadius() const; // return radius
17
18 double getDiameter() const; // return diameter
19 double getCircumference() const; // return circumference
20 double getArea() const; // return area
21
22 void print() const; // output Circle2 object
23
24 private:
25 double radius; // Circle2's radius
26
27 }; // end class Circle2
28
29 #endif
Fig. 9.11 Private base-class data cannot be accessed from derived class. (Part 1 of 3.)
624 Object-Oriented Programming: Inheritance Chapter 9
24
25 // return radius
26 double Circle2::getRadius() const
27 {
28 return radius;
29
30 } // end function getRadius
31
32 // calculate and return diameter
33 double Circle2::getDiameter() const
34 {
35 return 2 * radius;
36
37 } // end function getDiameter
38
39 // calculate and return circumference
40 double Circle2::getCircumference() const
41 {
42 return 3.14159 * getDiameter();
43
44 } // end function getCircumference
45
46 // calculate and return area
47 double Circle2::getArea() const
48 {
49 return 3.14159 * radius * radius;
50
51 } // end function getArea
52
53 // output Circle2 object
54 void Circle2::print() const
55 {
56 cout << "Center = [" << x << ", " << y << ']'
57 << "; Radius = " << radius;
58
59 } // end function print
Fig. 9.11 Private base-class data cannot be accessed from derived class. (Part 2 of 3.)
Chapter 9 Object-Oriented Programming: Inheritance 625
Fig. 9.11 Private base-class data cannot be accessed from derived class. (Part 3 of 3.)
4
5 using std::cout;
6
7 #include "point2.h" // Point2 class definition
8
9 // default constructor
10 Point2::Point2( int xValue, int yValue )
11 {
12 x = xValue;
13 y = yValue;
14
15 } // end Point2 constructor
16
17 // set x in coordinate pair
18 void Point2::setX( int xValue )
19 {
20 x = xValue; // no need for validation
21
22 } // end function setX
23
24 // return x from coordinate pair
25 int Point2::getX() const
26 {
27 return x;
28
29 } // end function getX
30
31 // set y in coordinate pair
32 void Point2::setY( int yValue )
33 {
34 y = yValue; // no need for validation
35
36 } // end function setY
37
38 // return y from coordinate pair
39 int Point2::getY() const
40 {
41 return y;
42
43 } // end function getY
44
45 // output Point2 object
46 void Point2::print() const
47 {
48 cout << '[' << x << ", " << y << ']';
49
50 } // end function print
Class Circle3 (Fig. 9.14Fig. 9.15) is a modification of class Circle2 (Fig. 9.10
Fig. 9.11) that inherits from class Point2 rather than from class Point. Because class
Circle3 inherits from class Point2, objects of class Circle3 can access inherited
Chapter 9 Object-Oriented Programming: Inheritance 627
data members that were declared protected in class Point2 (i.e., data members x and
y). As a result, the compiler does not generate errors when compiling the Circle3 con-
structor and print member function definitions in Fig. 9.15 (lines 1016 and 5459,
respectively). This shows the special privileges that a derived class is granted to access
protected base-class data members. Objects of a derived class also can access pro-
tected members in any of that derived classs indirect base classes.
Fig. 9.15 Circle3 class that inherits from class Point2. (Part 1 of 2.)
628 Object-Oriented Programming: Inheritance Chapter 9
13 y = yValue;
14 setRadius( radiusValue );
15
16 } // end Circle3 constructor
17
18 // set radius
19 void Circle3::setRadius( double radiusValue )
20 {
21 radius = ( radiusValue < 0.0 ? 0.0 : radiusValue );
22
23 } // end function setRadius
24
25 // return radius
26 double Circle3::getRadius() const
27 {
28 return radius;
29
30 } // end function getRadius
31
32 // calculate and return diameter
33 double Circle3::getDiameter() const
34 {
35 return 2 * radius;
36
37 } // end function getDiameter
38
39 // calculate and return circumference
40 double Circle3::getCircumference() const
41 {
42 return 3.14159 * getDiameter();
43
44 } // end function getCircumference
45
46 // calculate and return area
47 double Circle3::getArea() const
48 {
49 return 3.14159 * radius * radius;
50
51 } // end function getArea
52
53 // output Circle3 object
54 void Circle3::print() const
55 {
56 cout << "Center = [" << x << ", " << y << ']'
57 << "; Radius = " << radius;
58
59 } // end function print
Fig. 9.15 Circle3 class that inherits from class Point2. (Part 2 of 2.)
Class Circle3 does not inherit class Point2s constructor. However, class
Circle3s constructor (lines 1016) calls class Point2s constructor implicitly. In fact,
the first task of any derived-class constructor is to call its direct base classs constructor,
either implicitly or explicitly. (The syntax for calling a base-class constructor is discussed
Chapter 9 Object-Oriented Programming: Inheritance 629
later in this section.) If the code does not include an explicit call to the base-class con-
structor, an implicit call is made to the base classs default constructor. Even though lines
1213 set x and y values explicitly, the constructor first calls the Point2 default con-
structor, which initializes these data members to their default 0 values. Thus, x and y each
are initialized twice. We will fix this performance problem in the next examples.
Figure 9.16 performs identical tests on class Circle3 as those that Fig. 9.9 performed
on class Circle (Fig. 9.7Fig. 9.8). Note that the outputs of the two programs are identical.
We created class Circle without using inheritance and created class Circle3 using
inheritance; however, both classes provide the same functionality. Note that the code listing
for class Circle3 (i.e., the header and implementation files), which is 88 lines, is consid-
erably shorter than the code listing for class Circle, which is 122 lines, because class
Circle3 absorbs part of its functionality from Point2, whereas class Circle does not
absorb any functionality. Also, there is now only one copy of the point functionality men-
tioned in class Point2. This makes the code easier to debug, maintain and modify, because
the point-related code exists only in the files of Fig. 9.12Fig. 9.13.
Fig. 9.16 Protected base-class data can be accessed from derived class. (Part 1 of 2.)
630 Object-Oriented Programming: Inheritance Chapter 9
X coordinate is 37
Y coordinate is 43
Radius is 2.5
Fig. 9.16 Protected base-class data can be accessed from derived class. (Part 2 of 2.)
to derived classes. (Of course, if the base-class services change, we must reimplement our
derived classes, but good object-oriented design attempts to prevent this.)
Software Engineering Observation 9.3
It is appropriate to use the protected access specifier when a base class should provide
a service (i.e., a member function) only to its derived classes and should not provide the ser-
vice to other clients. 9.3
8 public:
9 Point3( int = 0, int = 0 ); // default constructor
10
11 void setX( int ); // set x in coordinate pair
12 int getX() const; // return x from coordinate pair
13
14 void setY( int ); // set y in coordinate pair
15 int getY() const; // return y from coordinate pair
16
17 void print() const; // output Point3 object
18
19 private:
20 int x; // x part of coordinate pair
21 int y; // y part of coordinate pair
22
23 }; // end class Point3
24
25 #endif
Fig. 9.18 Point3 class uses member functions to manipulate its private data.
(Part 1 of 2.)
Chapter 9 Object-Oriented Programming: Inheritance 633
Fig. 9.18 Point3 class uses member functions to manipulate its private data.
(Part 2 of 2.)
Class Circle4 (Fig. 9.19Fig. 9.20) has several changes to its member function
implementations (Fig. 9.20) that distinguish it from class Circle3 (Fig. 9.14Fig. 9.15).
Class Circle4s constructor (lines 1015) introduces base-class initializer syntax (line
11), which uses a member initializer to pass arguments to the base-class (Point3) con-
structor. C++ actually requires a derived-class constructor to call its base-class constructor
to initialize the base-class data members that are inherited into the derived class. Line 11
accomplishes this task by invoking the Point3 constructor by name. Values xValue and
yValue are passed from the Circle4 constructor to the Point3 constructor to initialize
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
default values for x and y (i.e., 0 and 0). If class Point3 did not provide a default con-
structor, the compiler would issue a syntax error.
Common Programming Error 9.1
It is a syntax error if a derived-class constructor calls one of its base-class constructors with
arguments that do not match exactly the number and types of parameters specified in one of
the base-class constructor definitions. 9.1
5
6 #include "point3.h" // Point3 class definition
7
8 class Circle4 : public Point3 {
9
10 public:
11
12 // default constructor
13 Circle4( int = 0, int = 0, double = 0.0 );
14
15 void setRadius( double ); // set radius
16 double getRadius() const; // return radius
17
18 double getDiameter() const; // return diameter
19 double getCircumference() const; // return circumference
20 double getArea() const; // return area
21
22 void print() const; // output Circle4 object
23
24 private:
25 double radius; // Circle4's radius
26
27 }; // end class Circle4
28
29 #endif
Fig. 9.20 Circle4 class that inherits from class Point3, which does not provide
protected data. (Part 1 of 2.)
Chapter 9 Object-Oriented Programming: Inheritance 635
24 // return radius
25 double Circle4::getRadius() const
26 {
27 return radius;
28
29 } // end function getRadius
30
31 // calculate and return diameter
32 double Circle4::getDiameter() const
33 {
34 return 2 * getRadius();
35
36 } // end function getDiameter
37
38 // calculate and return circumference
39 double Circle4::getCircumference() const
40 {
41 return 3.14159 * getDiameter();
42
43 } // end function getCircumference
44
45 // calculate and return area
46 double Circle4::getArea() const
47 {
48 return 3.14159 * getRadius() * getRadius();
49
50 } // end function getArea
51
52 // output Circle4 object
53 void Circle4::print() const
54 {
55 cout << "Center = ";
56 Point3::print(); // invoke Point3's print function
57 cout << "; Radius = " << getRadius();
58
59 } // end function print
Fig. 9.20 Circle4 class that inherits from class Point3, which does not provide
protected data. (Part 2 of 2.)
Performance Tip 9.2
In a derived-class constructor, initializing member objects and invoking base-class construc-
tors explicitly in the member initializer list can prevent duplicate initialization in which a de-
fault constructor is called, then data members are modified again in the body of the derived-
class constructor. 9.2
private data members x and y of class Point3 by calling base-class Point3s print
function with the expression Point3::print() (line 56). Note the syntax used to invoke
a redefined base-class member function from a derived classplace the base-class name
and the binary scope-resolution operator (::) before the base-class member-function name.
This member-function invocation is a good software engineering practice: Recall that Soft-
ware Engineering Observation 6.19 stated that, if an objects member function performs the
actions needed by another object, call that member function rather than duplicating its code
body. By having Circle4s print function invoke Point3s print function to per-
form part of the task of printing a Circle4 object (i.e., to display the x- and y-coordinate
values), we avoid duplicating code and reduce code-maintenance problems.
Common Programming Error 9.2
When a base-class member function is redefined in a derived class, the derived-class version
often calls the base-class version to do additional work. Failure to use the :: reference (pre-
fixed with the name of the base class) when referencing the base classs member function
causes infinite recursion, because the derived-class member function would then call itself. 9.2
Figure 9.21 performs identical manipulations on a Circle4 object as did Fig. 9.9 and
Fig. 9.16 on objects of classes Circle and Circle3, respectively. Although each circle
class behaves identically, class Circle4 is the best engineered. Using inheritance, we have
efficiently and effectively constructed a well-engineered class.
Fig. 9.21 Base-class private data is accessible to a derived class via public or
protected member function inherited by the derived class. (Part 1 of 2.)
Chapter 9 Object-Oriented Programming: Inheritance 637
23
24 circle.setX( 2 ); // set new x-coordinate
25 circle.setY( 2 ); // set new y-coordinate
26 circle.setRadius( 4.25 ); // set new radius
27
28 // display new circle value
29 cout << "\n\nThe new location and radius of circle are\n";
30 circle.print();
31
32 // display floating-point values with 2 digits of precision
33 cout << fixed << setprecision( 2 );
34
35 // display Circle4's diameter
36 cout << "\nDiameter is " << circle.getDiameter();
37
38 // display Circle4's circumference
39 cout << "\nCircumference is " << circle.getCircumference();
40
41 // display Circle4's area
42 cout << "\nArea is " << circle.getArea();
43
44 cout << endl;
45
46 return 0; // indicates successful termination
47
48 } // end main
X coordinate is 37
Y coordinate is 43
Radius is 2.5
The new location and radius of circle are
Center = [2, 2]; Radius = 4.25
Diameter is 8.50
Circumference is 26.70
Area is 56.74
Fig. 9.21 Base-class private data is accessible to a derived class via public or
protected member function inherited by the derived class. (Part 2 of 2.)
output. Class Cylinder also includes member function getVolume (lines 4145) to cal-
culate the cylinders volume.
Figure 9.24 is a CylinderTest application that tests class Cylinder. Line 18
instantiates a Cylinder object called cylinder. Lines 2124 use cylinders
Fig. 9.23 Cylinder class inherits from class Circle4 and redefines member
function getArea. (Part 1 of 2.)
640 Object-Oriented Programming: Inheritance Chapter 9
Fig. 9.23 Cylinder class inherits from class Circle4 and redefines member
function getArea. (Part 2 of 2.)
X coordinate is 12
Y coordinate is 23
Radius is 2.5
Height is 5.7
Diameter is 8.50
Circumference is 26.70
Area is 380.53
Volume is 567.45
Using the point/circle/cylinder example, we have shown the use and benefits of inher-
itance. We were able to develop classes Circle4 and Cylinder much more quickly by
using inheritance than if we had developed these classes from scratch. Inheritance avoids
duplicating code and the associated code-maintenance problems.
642 Object-Oriented Programming: Inheritance Chapter 9
When a derived-class object is destroyed, the program then calls that objects
destructor. This begins a chain of destructor calls in which the derived-class destructor and
the destructors of the direct and indirect base classes execute in reverse of the order in
which the constructors executed. When a derived-class objects destructor is called, the
destructor performs its task, then invokes the destructor of the next base class in the hier-
archy. This process repeats until the destructor of the final base class at the top of the hier-
archy is called. Then the object is removed from memory.
Software Engineering Observation 9.7
Suppose that 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 constructors for the base classs member objects execute, then the base-class constructor
executes, then the constructors for the derived classs member objects execute, then the de-
rived classs constructor executes. Destructors are called in the reverse of the order in which
their corresponding constructors are called. 9.7
Fig. 9.26 Point4 base class contains a constructor and a destructor. (Part 1 of 2.)
644 Object-Oriented Programming: Inheritance Chapter 9
24 print();
25 cout << endl;
26
27 } // end Point4 destructor
28
29 // set x in coordinate pair
30 void Point4::setX( int xValue )
31 {
32 x = xValue; // no need for validation
33
34 } // end function setX
35
36 // return x from coordinate pair
37 int Point4::getX() const
38 {
39 return x;
40
41 } // end function getX
42
43 // set y in coordinate pair
44 void Point4::setY( int yValue )
45 {
46 y = yValue; // no need for validation
47
48 } // end function setY
49
50 // return y from coordinate pair
51 int Point4::getY() const
52 {
53 return y;
54
55 } // end function getY
56
57 // output Point4 object
58 void Point4::print() const
59 {
60 cout << '[' << getX() << ", " << getY() << ']';
61
62 } // end function print
Fig. 9.26 Point4 base class contains a constructor and a destructor. (Part 2 of 2.)
Class Circle5 (Fig. 9.27Fig. 9.28) contains features from class Circle4
(Fig. 9.19Fig. 9.20). We modified the constructor (lines 1120 of Fig. 9.28) and included
a destructor (lines 2329), each of which outputs a line of text upon its invocation.
5
6 #include "point4.h" // Point4 class definition
7
8 class Circle5 : public Point4 {
9
10 public:
11
12 // default constructor
13 Circle5( int = 0, int = 0, double = 0.0 );
14
15 ~Circle5(); // destructor
16 void setRadius( double ); // set radius
17 double getRadius() const; // return radius
18
19 double getDiameter() const; // return diameter
20 double getCircumference() const; // return circumference
21 double getArea() const; // return area
22
23 void print() const; // output Circle5 object
24
25 private:
26 double radius; // Circle5's radius
27
28 }; // end class Circle5
29
30 #endif
Fig. 9.28 Circle5 class inherits from class Point4. (Part 1 of 2.)
646 Object-Oriented Programming: Inheritance Chapter 9
21
22 // destructor
23 Circle5::~Circle5()
24 {
25 cout << "Circle5 destructor: ";
26 print();
27 cout << endl;
28
29 } // end Circle5 destructor
30
31 // set radius
32 void Circle5::setRadius( double radiusValue )
33 {
34 radius = ( radiusValue < 0.0 ? 0.0 : radiusValue );
35
36 } // end function setRadius
37
38 // return radius
39 double Circle5::getRadius() const
40 {
41 return radius;
42
43 } // end function getRadius
44
45 // calculate and return diameter
46 double Circle5::getDiameter() const
47 {
48 return 2 * getRadius();
49
50 } // end function getDiameter
51
52 // calculate and return circumference
53 double Circle5::getCircumference() const
54 {
55 return 3.14159 * getDiameter();
56
57 } // end function getCircumference
58
59 // calculate and return area
60 double Circle5::getArea() const
61 {
62 return 3.14159 * getRadius() * getRadius();
63
64 } // end function getArea
65
66 // output Circle5 object
67 void Circle5::print() const
68 {
69 cout << "Center = ";
70 Point4::print(); // invoke Point4's print function
71 cout << "; Radius = " << getRadius();
72
73 } // end function print
Fig. 9.28 Circle5 class inherits from class Point4. (Part 2 of 2.)
Chapter 9 Object-Oriented Programming: Inheritance 647
Figure 9.29 demonstrates the order in which constructors and destructors are called for
objects of classes that are part of an inheritance hierarchy. Function main (lines 1129)
begins by instantiating a Point4 object (line 15) in a separate block inside main (lines 13
17). The object goes in and out of scope immediately (the end of the block is reached as soon
as the object is created), so both the Point4 constructor and destructor are called. Next,
line 20 instantiates Circle5 object circle1. This invokes the Point4 constructor to
perform output with values passed from the Circle5 constructor, then performs the output
specified in the Circle5 constructor. Line 23 then instantiates Circle5 object
circle2. Again, the Point4 and Circle5 constructors are both called. Note that, in
each case, the body of the Point4 constructor is executed before the body of the
Circle5 constructor executes. When the end of main is reached, the destructors are
called for objects circle1 and circle2. But, because destructors are called in the
reverse order of their corresponding constructors, the Circle5 destructor and Point4
destructor are called (in that order) for object circle2, then the Circle5 and Point4
destructors are called (in that order) for object circle1.
explosive-growth industry with the arrival of the personal computer, so, too, is the creation
and sale of class libraries. Application designers build their applications with these
libraries, and library designers are being rewarded by having their libraries included with
the applications. The standard C++ libraries that are shipped with C++ compilers tend to be
rather general purpose and limited in scope. However, there is massive worldwide commit-
ment to the development of class libraries for a huge variety of applications arenas.
Software Engineering Observation 9.8
At the design stage in an object-oriented system, the designer often determines that certain
classes are closely related. The designer should factor out common attributes and behav-
iors and place these in a base class. Then use inheritance to form derived classes, endowing
them with capabilities beyond those inherited from the base class. 9.8
Reading derived-class definitions can be confusing, because inherited members are not
shown physically in the derived class, but nevertheless are present in the derived classes. A
similar problem exists when documenting derived-class members.
In this chapter, we introduced inheritancethe ability to create classes by absorbing
an existing classs data members and member functions, and embellishing these with new
capabilities. In Chapter 10, we build upon our discussion of inheritance by introducing
polymorphisman object-oriented technique that enables us to write programs that handle,
in a more general manner, a wide variety of classes related by inheritance. After studying
Chapter 10, you will be familiar with classes, encapsulation, inheritance and polymor-
phismthe most crucial aspects of object-oriented programming.
pressed = true;
cout << "elevator button tells elevator to prepare to leave"
<< endl;
elevatorRef.prepareToLeave( true );
pressed = true;
cout << "floor " << floorNumber
<< " button summons elevator" << endl;
elevatorRef.summonElevator( floorNumber );
The first line of each block of code is identical, but the remaining sections are different.
Therefore, each derived class must redefine the base-class Button member function
pressButton.
ElevatorButton FloorButton
- pressed : Boolean = false - pressed : Boolean = false
- floorNumber : Integer
+ pressButton( ) + pressButton( )
+ resetButton( ) + resetButton( )
Clock
1
1 1
Building
1
1
Scheduler
1
1
Creates *
0..1 0..1
Person
occupant passenger
{xor}
2 1 1
2 Services 1
Floor Elevator
2 1
1 1 1
Summons
1 1 2 1 1
Light FloorButton ElevatorButton Bell
1
Door
Button
Figure 9.33 lists the header file for the base class Button.2 We declare public
member functions pressButton and resetButton (lines 1314) and private data
member pressed of type bool (line 22). Notice the declaration of the reference to an
Elevator object in line 19 and the corresponding parameter to the constructor in line 11.
We show how to initialize the reference when we discuss the code for the derived classes.
The derived classes perform two different actions. Class ElevatorButton invokes
the prepareToLeave member function of class Elevator; class FloorButton
invokes the summonElevator member function. Thus, both classes need access to the
2. The benefit of encapsulation is that no other files in our elevator simulation need to be changed.
We simply substitute the new elevatorButton and floorButton header and implementa-
tion files for the old ones and add the files for class Button.
Chapter 9 Object-Oriented Programming: Inheritance 653
elevatorRef data member of the base class; however, this data member should not be
available to non-Button objects. Therefore, we place the elevatorRef data member
in the protected section of Button. Only base-class member functions directly manip-
ulate data member pressed, so we declare this data member as private. Derived
classes do not need to access pressed directly.
Figure 9.34 lists the implementation file for class Button. Line 12 in the constructor
initializes the reference to the elevator. The constructor and destructor display messages
indicating that they are running, and the pressButton and resetButton member
functions manipulate private data member pressed.
10 // constructor
11 Button::Button( Elevator &elevatorHandle )
12 : elevatorRef( elevatorHandle ), pressed( false )
13 {
14 cout << "button constructed" << endl;
15
16 } // end Button constructor
17
18 // destructor
19 Button::~Button()
20 {
21 cout << "button destructed" << endl;
22
23 } // end Button destructor
24
25 // press button
26 void Button::pressButton()
27 {
28 pressed = true;
29
30 } // end function pressButton
31
32 // reset button
33 void Button::resetButton()
34 {
35 pressed = false;
36
37 } // end function resetButton
Figure 9.35 contains the header file for class ElevatorButton. Line 8 indicates
that the class inherits from class Button. This inheritance means that class Elevator-
Button contains the protected elevatorRef data member and the public press-
Button and resetButton member functions of the base class. In line 13, we provide
a function prototype for pressButton to signal our intent to redefine that member func-
tion in the .cpp file. We discuss the pressButton implementation momentarily.
The constructor takes as a parameter a reference to class Elevator (line 11). We dis-
cuss the necessity for this parameter when we discuss the classs implementation. Notice,
however, that we do not need to include a forward declaration of class Elevator in the
derived class, because the base-class header file contains the forward reference.
7
8 class ElevatorButton : public Button {
9
10 public:
11 ElevatorButton( Elevator & ); // constructor
12 ~ElevatorButton(); // destructor
13 void pressButton(); // press the button
14
15 }; // end class ElevatorButton
16
17 #endif // ELEVATORBUTTON_H
10
11 // constructor
12 FloorButton::FloorButton( int floor, Elevator &elevatorHandle )
13 : Button( elevatorHandle ),
14 floorNumber( floor )
15 {
16 cout << "floor " << floorNumber << " button constructed"
17 << endl;
18
19 } // end FloorButton constructor
20
21 // destructor
22 FloorButton::~FloorButton()
23 {
24 cout << "floor " << floorNumber << " button destructed"
25 << endl;
26
27 } // end ~FloorButton destructor
28
29 // press the button
30 void FloorButton::pressButton()
31 {
32 Button::pressButton();
33 cout << "floor " << floorNumber
34 << " button summons elevator" << endl;
35
36 // call elevator to this floor
37 elevatorRef.summonElevator( floorNumber );
38
39 } // end function pressButton
We now have completed the implementation for the elevator-simulator case study that
we have been developing since Chapter 2. One significant architectural opportunity
remains. You might have noticed that classes Button, Door and Light have much in
common. Each of these classes contains a state attribute and corresponding set on and
set off operations. Class Bell also bears some similarity to these other classes. Object-
oriented thinking tells us that we should place commonalities in one or more base classes,
from which we should then use inheritance to form appropriate derived classes. We leave
this implementation to the reader as an exercise. We suggest that you begin by modifying
the class diagram in Fig. 9.32. [Hint: Button, Door and Light are essentially toggle
classesthey each have state, set on and set off capabilities; Bell is a thinner
class, with only a single operation and no state.]
We sincerely hope that this elevator simulation case study was a challenging and
meaningful experience for you. We employed a carefully developed, incremental object-
oriented process to produce a UML-based design for our elevator simulator. From this
design, we produced a substantial working C++ implementation using key programming
notions, including classes, objects, encapsulation, visibility, composition and inheritance.
658 Object-Oriented Programming: Inheritance Chapter 9
In the remaining chapters of the book, we present many additional key C++ technologies.
We would be grateful if you would take a moment to send your comments, criticisms and
suggestions for improving this case study to us at [email protected].
SUMMARY
Software reuse reduces program-development time.
The direct base class of a derived class is the base class from which the derived class inherits (spec-
ified by the class name to the right of the : in the first line of a class definition). An indirect base
class of a derived class is two or more levels up the class hierarchy from that derived class.
With single inheritance, a class is derived from one base class. With multiple inheritance, a class
is derived from more than one direct base class.
A derived class can include its own data members and member functions, so a derived class is of-
ten larger than its base class.
A derived class is more specific than its base class and represents a smaller group of objects.
Every object of a derived class is also an object of that classs base class. However, a base-class
object is not an object of that classs derived classes.
Derived-class member functions can access protected base-class members directly.
An is-a relationship represents inheritance. In an is-a relationship, an object of a derived class
also can be treated as an object of its base class.
A has-a relationship represents composition. In a has-a relationship, a class object contains
one or more objects of other classes as members.
A derived class cannot access the private members of its base class directly; 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 directly.
When a base-class member function is inappropriate for a derived class, that member function can
be redefined in the derived class with an appropriate implementation.
Single-inheritance relationships form tree-like hierarchical structuresa base class exists in a hi-
erarchical relationship with its derived classes.
It is possible to treat base-class objects and derived-class objects similarly; the commonality
shared between the object types is expressed in the data members and member functions of the
base class.
A base classs public members are accessible anywhere that the program has a handle to an object
of that base class or to an object of one of that base classs derived classes.
A base classs private members are accessible only within the definition of that base class or from
friends of that class.
A base classs protected members have an intermediate level of protection between public and pri-
vate access. A base classs protected members can be accessed by members and friends of that
base class and by members and friends of any classes derived from that base class.
Unfortunately, protected data members often yield two major problems. First, the derived-class
object does not have to use a set function to change the value of the base-classs protected data.
Second, derived-class member functions are more likely to depend on base-class implementation
details.
When a derived-class member function redefines a base-class member function, the base-class
member function can be accessed from the derived class by preceding the base-class member func-
tion name with the base-class name and the scope resolution operator (::).
Chapter 9 Object-Oriented Programming: Inheritance 659
When an object of a derived class is instantiated, the base classs constructor is called immediately
(either explicitly or implicitly) to initialize the base-class data members in the derived-class object
(before the derived-class data members are initialized).
Declaring data members private, while providing non-private member functions to manipulate
and perform validation checking on this data, enforces good software engineering.
When a derived-class object is destroyed, the destructors are called in the reverse order of the con-
structorsfirst the derived-class destructor is called, then the base-class destructor is called.
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.
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.
Knows a relationships are examples of objects containing pointers or references to other objects
so they can be aware of those objects.
TERMINOLOGY
abstraction inheritance
association is-a relationship
base class knows-a relationship
base-class constructor member access control
base-class default constructor member class
base-class destructor member object
base-class initializer multiple inheritance
class hierarchy object-oriented programming (OOP)
composition private base class
customize software private inheritance
derived class protected base class
derived-class constructor protected inheritance
derived-class destructor protected keyword
direct base class protected member of a class
friend of a base class public base class
friend of a derived class public inheritance
has-a relationship redefine a base-class member function
hierarchical relationship single inheritance
indirect base class software reusability
infinite recursion error uses-a relationship
SELF-REVIEW EXERCISES
9.1 Fill in the blanks in each of the following statements:
a) is a form of software reusability in which new classes absorb the data and
behaviors of existing classes and embellish these classes with new capabilities.
b) A base classs members can be accessed only in the base-class definition or
in derived-class definitions.
660 Object-Oriented Programming: Inheritance Chapter 9
EXERCISES
9.3 Many programs written with inheritance could be written with composition instead, and vice
versa. Rewrite classes Point3, Circle4 and Cylinder to use composition, rather than inherit-
ance. After you do this, assess the relative merits of the two approaches for the Point3, Circle4,
Cylinder problem, as well as for object-oriented programs in general. Which approach is more nat-
ural, why?
9.4 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. using private access
in base classes.
9.5 Rewrite the case study in Section 9.5 as a Point, Square, Cube program. Do this two
waysonce via inheritance and once via composition.
Chapter 9 Object-Oriented Programming: Inheritance 661