09 Inheritance
09 Inheritance
12
9. OOP & ADTs: Introduction to
Inheritance
A. Inheritance, OOD, and OOP (§12.1 & 12.2)
A major objective of OOP: writing reusable code
(to avoid re-inventing the wheel).
Ways to do this in C++:
Encapsulate code within functions
Build classes
Store classes and functions in separately-compiled
libraries
Convert functions into type-parameterized function
templates
Convert classes into type-parameterized class
templates
An additional approach that distinguishes OOP from the
1
others:
Example: Suppose a problem requires stack operations not
provided in our stack class — e.g., max(), min()
RevStack
class
new operations
including revised
push(), pop(), ...
Stack stObj
push(), pop(), ...
myTop, ...
new data members
Better, but:
A RevStack is not a Stack; it has a Stack.
3
#3: Copy-&-paste approach: Build a new RevStack
class, copying and pasting the data members and
function members of Stack into RevStack .
5
Object-oriented design (OOD) is to engineer one’s
software as follows:
1. Identify the objects in the problem
2. Look for commonality in those objects
3. Define base classes containing that commonality
4. Define derived classes that inherit from the base class
These last two steps are the most difficult aspects of OOD.
6
B. Derived Classes
Problem: Model various kinds of licenses.
Old Approach: Build separate classes for each license from
scratch
OOD: What attributes do all licenses have in common?
Then store these common attributes in a general (base) class
class License
License:
{
public:
// Function members Display(), Read(), ...
8
This inclusion technique works but it is a bit "clunky"
and inefficient; for example, we need to "double dot"
to access members of the included object:
DriversLicense h;
...
h.common.Display(cout);
Problem:
Private class members cannot be accessed outside
of
their class (except by friend functions), not even
within derived classes. 10
One C++ solution:
Members declared to be protected: can be accessed
within a derived class, but remain inaccessible to
programs or non-derived classes that use the class
(except for friend functions).
So change the private section in class License to a
protected section:
class License
{
public:
// Function members
// Display(), Read(), ...
protected:
long myNumber;
string myLastName,
myFirstName;
char myMiddleInitial;
int myAge;
Date myBirthDay;
...
11
};
Now we can derive classes for the more specialized licenses from
License:
class DriversLicense : public License
{
public:
...
protected:
int myVehicleType;
string myRestrictionsCode;
...
};
class HuntingLicense : public License
{
public:
...
protected: class PetLicense : public License
string thePrey; {
Date seasonBegin, public:
seasonEnd; ...
... protected:
}; string myAnimalType;
...
};
12
Classes like DriversLicense, HuntingLicense, and
PetLicense are said to be derived classes (or child
classes or subclasses),
and the class License from which they are derived is called a
base class (or parent class or superclass).
We have used protected sections rather than private ones in
these derived classes to make it possible to derive "second-
level" classes from these if/when it becomes necessary; for
example:
class MooseLicense : public HuntingLicense
{
public:
...
protected:
int theAntlerMaximum;
int theBullwinkleFactor;
...
};
13
This leads to class hierarchies — usually pictured as
a tree
but with arrows drawn from a derived class to its base
class: License
Other Properties:
They cannot access private members of the base
class.
Access to public and protected members of the base
class depends on the kind of inheritance specified in
the heading:
public public and protected,
respectively
private private 16
The most common is public inheritance; this is the only kind
we will use. It means that:
We can use the public and protected
members of the base class in a derived class
just as though they were declared in the
derived class itself.
It gives rise to the is-a relationship:
For class Derived : public Base
{
// ... members of Derived ...
};
every Derived object is a Base object.
class BusDriver
{
...
private:
License myLicense;// a bus driver has a license
...
};
Design Principle: Don't use public inheritance
for the
has-a relationship. 18
A third relationship between classes is the uses
relationship: One class might simply use another
class. For example, a Fee() member function in a
LicensePlate class might have a parameter of type
DriversLicense. But this class simply uses the
DriversLicense class — it is not a DriversLicense
and it does not have a DriversLicense.
20
3. Then repeat this approach “upwards” as
appropriate:
Attributes Common
to Object 1 thru Object n
Attributes Attributes
Common ... Common
to Object 1 to Object j
thru Object i thru Object n
21
Once no more commonality exists, OO implementation
then:
4. Proceeds from the top down, building the most general
base class(es): Attributes Common
to Object 1 thru Object n
Attributes Attributes
Common ... Common
to Object 1 to Object j
thru Object i thru Object n 22
6. Derivations continue until classes for the actual objects in
the
system are built:
Attributes Common
to Object 1 thru Object n
Attributes Attributes
Common ... Common
to Object 1 to Object j
thru Object i thru Object n
... ...
Object 1 Object i Object j Object n
24
3. Define a base class containing the common data
members:
class Employee
{
public:
// ... various Employee operations ...
protected:
long myIdNum; // Employee's id number
string myLastName, // " last name
myFirstName; // " first name
char myMiddleInitial; // " middle initial
int myDeptCode; // " department code
25
4. From the base class, derive classes containing special
attributes:
a. A salaried employee
class:
class SalariedEmployee : public Employee
{
public:
// ... salaried employee operations ...
protected:
double mySalary;
};
b. An hourly employee
class:
class HourlyEmployee : public Employee
{
public:
// ... hourly employee operations ...
protected:
double myWeeklyWage,
myHoursWorked,
myOverTimeFactor;
26
};
and others . . .
Employee
Execu- Super-
tive visor
27
All of the classes that have Employee as an ancestor
inherit the members (data and function) of Employee.
30
Is-a and Has-a Relationships: Skip?
33
General Principle of Derived Class Initialization
Use the base class constructor to initialize base class
members, and use the derived class constructor to
initialize derived class members.
, mySalary(sal)
char initial, int dept, double sal)
: Employee(id, last, first, initial, dept)
{
}
This is less commonly used than "normal"
initialization
datamem = initval; in the function body. It is 35
used when initialization is desired before the
D. Polymorphism
Consider:
class Employee
{
public:
Employee(long id = 0, string last = "", string first = "",
char initial = ' ', int dept = 0);
void Print(ostream & out) const;
// ... other Employee operations ...
protected:
// ... data members common to all Employees
};
// Definition of Print
void Employee::Print(ostream & out) const
{ . . . }
// Definition of output operator<<
ostream & operator<<(ostream & out, const Employee & emp)
{ emp.Print(out);
return out; } 36
And suppose we have overridden Print() for the subclasses
SalariedEmployee and HourlyEmployee as described
earlier.
The statements:
Employee emp(11111, "Doe", "John", 'J', 11);
SalariedEmployee empSal(22222, "Smith", "Mary", 'M', 22,
59900);
HourlyEmployee empHr(33333, "Jones", "Jay", 'J', 33, 15.25,
40);
cout << emp << endl << empSalnot:
<< endl << empHr << endl;
then
11111produce as Joutput:
Doe, John 11 11111 Doe, John J 11
class Employee
{
public:
// ...
virtual void Print(ostream & out) const;
// ... other Employee operations ...
private: 38
This works; operator<<() will use
LinkedList<Employee> L;
LinkedList<Employee *> L;
HourlyEmployee
derived
L from class Employee:
...