0% found this document useful (0 votes)
8 views

09 Inheritance

fgfsssssssssssss

Uploaded by

sultanahimed
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

09 Inheritance

fgfsssssssssssss

Uploaded by

sultanahimed
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 44

Read Chap.

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()

Ways to Approach this:


#1: Add function members to the Stack class that
implement the
new operations.
Stack class
new operations
push(), pop(), ...
myTop, ...
new data
members

Bad: This can easily mess up a tested, operational


class, creating problems for other client
programs. 2
#2: An adapter approach: Build a new RevStack class
that contains a Stack as a data member.

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 .

Stack class RevStack


push(), pop(), ... classoperations
new
cop
myTop, ... y push(), pop(), ...
myTop, ...
new data members

Almost right, but:


These are separate independent classes.
Modifying Stack (e.g., changing from an array to
a linked list for the stack elements) doesn't
automatically update a RevStack. 4
#4: Object-oriented approach:
Derive a new class RevStack from the Stack class,
which is called its parent class or base class.

This is the best:


(i) A derived class inherits all members of its parent class

(including its operations); we need not reinvent the


wheel.
(ii) Mistakes
(iii) Modifying the in
made Stack classRevStack
building automatically
class updates the
will be local
to it; RevStack class.
the original Stack class remains unchanged and
client
programs are not affected.

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.

Object-oriented programming (OOP) was first used to


describe the programming environment for Smalltalk, the
earliest true object-oriented programming
We've done this language. OOP
languages have three important properties:
 Inheritance
 Polymorphism, with the related concept of
 Encapsulation
dynamic or late binding

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(), ...

private: // we'll change this in a minute


long myNumber;
string myLastName,
myFirstName;
char myMiddleInitial;
int myAge;
Date myBirthDay; // Date is a user-defined
type
...
}; 7
We could include a data member of type License in each of
the classes for the various kinds of licenses and then add
new members:
class DriversLicense class HuntingLicense
{ {
public: public:
... ...
private: private:
License common; License common;
int myVehicleType; string thePrey;
string myRestrictionsCode; Date seasonBegin,
... seasonEnd;
}; ...
};
class PetLicense
{
public: This has-a relation (inclusion)
...
private:
defines containment; i.e., when
License common; one object contains an instance
string myAnimalType; of another object.
...
};

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);

Worse... Can one say that a driver’s license is a


license?
No! This is bad OOD.
Design should reflect reality not
implementation.
What we really want is the is-a relationship because 9
So we need: a DriversLicense is a License,

not a DriversLicense has a License.


 we need inheritance.

We will derive the specialized license classes


from the base class License and
add new members to store and operate on
their new attributes.

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

Drivers Hunting ... Pet


License License License

... ... ...

Car Unicycle Moose Dinosaur Dog Hamster


License License License License License License

Each non-root class inherits the members of its ancestor


classes.
This means that an attribute needs to be defined only
once
(at the appropriate level), allowing
JavaaAPI
programmer to 14
Usual Form of Declaration of a
Derived Class

DerivedClassName : public BaseClassName


{
...
// new data members and
// functions for derived class
...
}

(More generally, the keyword public can be


replaced by private or protected.)
15
The Fundamental Property of Derived
Classes
They inherit the members of the base class
(and thus the members of all ancestor classes).

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.

For example: A HuntingLicense is a License


A MooseLicense is a HuntingLicense
A MooseLicense is a License
17
The property that derived classes inherit the members of
ancestor classes can easily be misused. For example, it is
bad design to do the following just to get the members of
one class into another:
class BusDriver : public License
{ ... }

Rather, we should use:

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.

It isn't always easy to tell which is the appropriate


one to use. Two useful tests in deciding whether to
derive Y from X:
1. Do the operations in X behave properly in Y?
2. (The "need-a use-a" test): If all you need is a
Y, 19
can you use an X?
Summary:

The OOP approach to system design is to:


1. Carefully analyze the objects in a problem from the
bottom up.
2. Where commonality exists between objects, group the
common
attributes into a base class:
Attributes Attributes
Common Common
to Object 1 to Object j
thru Object i thru Object n
...
... ...
Object 1 Object i Object j Object n

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

5. The less-general classes are then derived (publicly) from


that
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

7. These classes can then be used to construct the


23
C. Another (Classic) Example: Employees

Problem: Design a payroll system.

Following the four OOD steps, we proceed as follows:


1. Identify the objects in the problem:
Salaried employees
Hourly employees
2. Look for commonality in those objects: what attributes
do they share?
Id number
Name
Department
...

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

// ... other members common to all Employees


};

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

Salaried Hourly Commission Contract


Employee Employee ed Employee
Employee

Pro- Consul- Secre- Tech


Manager Support Sales Reviewer Editor
grammer tant tary

Execu- Super-
tive visor

27
All of the classes that have Employee as an ancestor
inherit the members (data and function) of Employee.

For example, each HourlyEmployee and Manager object


is an Employee object so each contains the members
myIdNum, myLastName, myFirstName, and so on...

So, if Employee has a public operation to extract


myIdNum,
long IdNumber() const { return myIdNum; }
then it can be used by hourly employees and managers:
HourlyEmployee hourlyEmp;
Manager managerEmp;
...
cout << hourlyEmp.IdNumber() << endl 28
Reusability:
Suppose we define an output function member in
Employee:
void Employee::Print(ostream & out) const
{
out << myIdNum << ' ' << myLastName << ", "
<< myFirstName << ' ' << myMiddleNInitial
Not << " "
<< myDeptCode << endl; "overload"
}

In derived classes, we can override Print() with new


definitions that reuse the Print() function of class
Employee.A class Deriv derived from class Base
can call
Base::F()
to reuse the work of the member
function F() from the base class.
29
For example:

void SalariedEmployee::Print(ostream & out) const


{
Employee::Print(out); //inherited members
out << "$" << mySalary << endl; //local members
}

void HourlyEmployee::Print(ostream & out) const


{
Employee::Print(out); //inherited members
out << "$" << myWeeklyWage << endl //local members
<< myHoursWorked << endl
<< myOverTimeFactor << endl;
}

30
Is-a and Has-a Relationships: Skip?

An object can participate in is-a and has-a relationships


simultaneously.
For example, we could define an address class
class Address
{
public:
string street,
city, state,
zip;
}
and use it to declare some data member in the class
Employee.
Then, for example, a SalariedEmployee is-an Employee
31
Constructors and Inheritance

Consider Employee's constructor


// Explicit-Value Constructor
inline Employee::Employee(long id, string last,
string first, char initial,
int dept)
{
myIdNum = id;
myLastName = last;
myFirstName = first;
myMiddleInitial = initial;
myDeptCode = dept;
}

A derived class can use a member-


initialization-list
to call the base-class constructor to initialize the
inherited 32
data members — easier than writing it from
// Definition of SalariedEmployee explicit-value constructor
inline SalariedEmployee::SalariedEmployee
(long id, string last, string first,
char initial, int dept, double sal)
: Employee(id, last, first, initial, dept)
{
mySalary = sal;
}

From the derived class constructor, pass the id


number, last name, first name, middle initial and
dept. code to the Employee constructor to initialize
those members.
SalariedEmployee()then initializes only its own
(local) member(s).

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.

General form of Member-Initialization-List


Mechanism:
Derive::Derive(ParameterList) : Base(ArgList)
{
// initialize the non-inherited members
// in the usual manner ...
}
Initializations in a member-initialization-list are done
first, before those in the body of the constructor 34
Member-initialization list can also be used to initialize
new data members in the derived class:
Data member datamem of a derived class can be
initialized to
an initial value initval using the unusual function
notation
datamem(initval) in the member-initialization list.
inline SalariedEmployee::SalariedEmployee
Example: (long id, string last, string first,

, 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

22222 Smith, Mary M 22 22222 Smith, Mary M 22


$59900
33333 Jones, Jay J 33
33333 Jones, Jay J 33
$15.25
40
1.5
37
This is because the call to Print() in the definition of
operator<<() uses Employee's Print(). What we need is
dynamic orDon't bind a definition of a function member
to a call to that function
late binding:
until runtime.

This is accomplished by declaring Print() to be a virtual


function by
prepending the keyword virtual to its prototype in the
Employee class:

class Employee
{
public:
// ...
virtual void Print(ostream & out) const;
// ... other Employee operations ...
private: 38
This works; operator<<() will use

Employee::Print() for Employees


SalariedEmployee::Print() for SalariedEmployees
HourlyEmployee::Print() for HourlyEmployees

The same function call can cause different effects at different


times
— has many forms — based on the function to which the call
Polymorphism is another important
is bound.
advantage
Such calls of inheritance
are described in OOP
as polymorphic (Greek for "many
forms").languages.
Thanks to polymorphism, we can apply operator<<() to
derived class objects without explicitly overloading it for
those objects.
Note: This is a good reason to define operator<<() so
that it calls some output function member of a
class rather than making it a friend function.
39
Another Example:
 A base-class pointer can point to any derived class
object.
So consider a declaration
Employee * eptr;
Since a SalariedEmployee is-an Employee, eptr can point to
a SalariedEmployee object:
eptr = new SalariedEmployee;
Similarly, it can point to an HourlyEmployee object:
eptr = new HourlyEmployee;
For the call
eptr->Print(cout);
to work, SalariedEmployee::Print(cout) must be used
when eptr points to a SalariedEmployee, but
HourlyEmployee::
Print(cout) must be used when eptr points to
HourlyEmployee.
Here is another instance where Print() must be a virtual
function so that this function call can be bound to different 40
By declaring a base-class function member to
be virtual, a derived class can override that
function so that calls to it though a pointer or
reference will be bound (at run-time) to the
appropriate definition.
If we want to force derived classes to provide
definitions of some virtual function, we make it a
pure virtual function
— also called an abstract function — and the class
is
called an abstract class .
This is accomplished in C++ by attaching = 0 to
the function's prototype:
virtual PrototypeOfFunction = 0;
41
No definition of the function is given in the base
E.Hetergeneous Data Structures
Consider a LinkedList of Employee objects:

LinkedList<Employee> L;

Each node of L will only have space for an Employee, with


no space for the additional data of an hourly or salaried
employee:
L
n emp1 emp2 emp_n
...

Such a list is a homogeneous structure: Each


value in the list must be of the same type
(Employee). 42
Now suppose we make L a LinkedList of Employee
pointers:

LinkedList<Employee *> L;
HourlyEmployee

Then each node of L can store a pointer to


Employee any object
SalariedEmployee

derived
L from class Employee:

n emp1 emp2 emp_n

...

Thus, salaried and hourly employees can be


intermixed in the same list, and we have a
43
heterogeneous storage structure.
Now consider:
Node * nPtr = L.first;
while (nPtr != 0)
{
nptr->data->Print(cout);
nptr = nPtr->next;
}
For the call
nPtr->data->Print(cout);
to work when nPtr->data points to a SalariedEmployee
object,
SalariedEmployee::Print() within that object must be
called;
but when nPtr->data is a pointer to an HourlyEmployee,
HourlyEmployee::Print() within that object must be
44
called.

You might also like