chapter 14 - inheritancewps.aw.com/wps/media/objects/80/82902/savitchimch14.doc · web viewchapter...
TRANSCRIPT
Chapter 14
Inheritance
0. Introduction
Object-Oriented Programming (OOP) is a powerful programming technique. This
technique is of great importance for several reasons. OOP enables the solution domain to
be closer to the problem domain, in part, by providing solution domain modeling for
actors and behavior in problems. It facilitates code reuse, and it helps the programmer to
better manage complexity through better abstraction and encapsulation.
In our earlier study of data abstraction, we identified objects and classes. In C++, an
object is a variable that has functions (behavior) and data associated with it. We noted
that classes encapsulate the essence of objects that are declared to be of class type. We
characterized inheritance as creating a new class by "adding features to the features
obtained from another class." We used the derivation of the ifstream class from
istream, and the derivation of ofstream from ostream as examples of inheritance.
We found that an object has identity (name, type), behavior (actions of the member
functions), and state (the values of the variables defined in the class and set on behalf of
our object.).
This chapter studies the inheritance relationship that classes bear to other, that is, the “is
a” relation. This chapter enables the student programmer to use inheritance in problem
solving.
1. Outline of topics in the chapter
14.1 Inheritance Basics
Derived Classes
Constructors in Derived Classes
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 2Chapter 14 Inheritance
The protected Qualifier
Redefinition of Member Functions
Redefining versus Overloading
Access to a Redefined Base Function
Functions That Are Not Inherited
14.2 Programming With Inheritance
Assignment Operators and Copy Constructors in Derived Classes
Destructors in Derive Classes
Protected and Private Inheritance
Multiple Inheritance
2. General remarks on the chapter
14.1 Inheritance Basics
In the real world, objects exist in relation to one another. In solving a problem, we need
to be as close to the problem as we can, within an abstraction that allows us to retain only
the details to necessary to meaningful solution of our problem. Abstraction means that we
ignore details. Ignoring details simplifies the model so that we can solve the problem.
Retaining details makes our solution realistic. A meaningful solution means we have
balanced the details ignored to decrease the complexity with the essential details retained.
In a problem, we may find that objects are similar to other objects. The abstraction of the
commonality among objects in C++ is done with classes, and abstraction of the
commonality among objects that have distinguishing differences is expressed in C++ by
the notion of inheritance. The commonalities are encapsulated in the base class, the
distinctions are encapsulated in the derived class or classes, and the commonality is
passed from the base class to the derived class by the inheritance mechanism.
For example, consider a class of (general) vehicles. We may think of several kinds of
vehicle whose properties we list here.
vehicle that carries passengers (a car),
small vehicle that carries cargo in the open (a pickup truck),
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 3Chapter 14 Inheritance
vehicle that carries cargo in an enclosure (Van),
vehicle that carries cargo enclosed and is articulated (A tractor-trailer).
The Car, Truck, Van, and TractorTrailer classes could inherit their common
features from the class vehicle. (Perhaps the common features of vehicles are
having a housing for the operator, a frame, wheels, a steering mechanism, a motor and
transmission.) We see that inheritance can creates a new class, called the derived class,
that is an extension, or modification of one or more existing classes, called the base
classes1.
We speak of single inheritance in the situation where there is only one base class.
Multiple inheritance refers to the situation where there is more than one base class. We
will not discuss multiple inheritance further as the text barely mentions this topic.
In these cases, a car is a vehicle, so are pickups, vans, and tractor-trailers. We describe
this relationship as an "is-a" relation. A car "is a" vehicle, a pickup truck "is a" vehicle,
and so on. The class abstraction of vehicle is the base class and the vehicles that bear the
"is a" relation to the base class are derived classes. Each base class has features, that the
derived class inherits those features and adds its own, extra, added features to the base
class to form its distinctiveness. If we write this example in C++, we haveclass Vehicle {/* . . . */};
class Car : public Vehicle { /* . . . */};
class Truck : public Vehicle { /* . . . */};
class TractorTrailer public Truck {/* . . . */};
class StationWagon : public Car {/* . . . */ };
class Boat : public Vehicle {/* . . .*/};
In this, example, we could illustrate multiple inheritance by defining
class Emergency {/* . . . */}; and have had a class PoliceCar
inherit from both class Car and class Emergency. We could have a
class Ambulance that inherits from both class Truck and
class EmergencyVehicle. The PoliceCar and Ambulance would inherit features
from class Car and the derived class adds its distinguishing features. The text only 1 In the C++ FAQ, pages 101 and 102, Cline and Lomow warn against use of “is a specialization of” and “is a subset of” as heuristics for detecting inheritance. Neither of “is a specialization of” or “is a subset of” implies that Derived must support all the operations defined by Base. The authors suggest substitutability as the proper heuristic.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 4Chapter 14 Inheritance
treats single inheritance, so we will not discuss this more general situation further. For
further information on multiple inheritance, see Lippman and Lajoie, C++ Primer, 3rd
edition or Stroustrup, The C++ Programming Language 3rd edition.
Terminology:
The roots of Object Oriented everything (Design, Analysis, Programming) are diverse.
The result is that terminology is equally diverse. From Simula 67 and Smalltalk come
object, class, superclass and subclass, methods and instance variables. From C++ we get
base class and derived class, member functions and member variables. From CLOS
(Common Lisp Object System) we get generic functions In C++ we have the template
facility.
The text follows Stroustrup’s usage in that it uses the terms base class and derived class,
member function and member variable, rather than superclass, subclass, method and
instance variable. Stroustrup remarks that he finds it counter intuitive to have a 'subclass'
that has more features than the 'superclass'.
Other terms that are used with derived class family relationships are described in the box,
“Parent and Child Classes” on page 590 of the text.
Access Specifiers in Inheritance
Suppose class derived has base class Base, as in the following code.
class Base {/* . . .*/};
class Derived : <access-specifier> Base
{ /* . . . */};
//where <access-specifier> is one of public, protected, private
Then the access that is accorded to member functions of class Derived depends on
the access-specifier selected. Unfortunately2, the access specifier for class inheritance
defaults to private3. My students sometimes forget the access specifier, with results that
2 “Unfortunately” is only an expression of my opinion. It is not to be confused with fact.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 5Chapter 14 Inheritance
are disappointing and confusing. Caution the students against omitting the access
specifier in inheritance.
It should be noted that while private and protected inheritance is possible, neither
is much used.
All Base Class Members Are Inherited
The private base class members are inaccessible to any function defined in any
derived class. For this reason, it is an error to attempt to access base class private
members. For purposes of writing code, private base class members might as well not
be inherited.
The foregoing apparently to the contrary, there is a very small number of exceptions to
the rule that every member of the base class is inherited, including the private
members. The exceptions to this are constructors, destructors, and certain operators
overloaded as members. We discuss this in a later section.
If you don’t believe that the private members are inherited, remember that all function
members of the base class are also inherited. These necessarily access the private
members of the base class, so these members must be present. You can confirm this.
Write a base class B with some private members (and other members), then derive a
class D with no members from the base class. You will find that the size of the derived
class object has the same size as the base class object.
If the derived class has members added of a size that would violate alignment that your
CPU requires, the implementation may allocate memory to “pad” accesses to achieve the
required alignment. For example, suppose you are using VC++ under Windows. If the
base class has only one int member, the base class object will have size 4. If the derived
class adds a char member and an int member in that order, the size of the derived class
3 The access specifier for struct defaults to public. The text treats a struct much as if structs had only the attributes they have in C. It does not use struct member functions, for example. We will not pursue the use of inheritance with structs.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 6Chapter 14 Inheritance
object will be 16. The derived class will inherit the two int variables from the base class
(8 bytes), add 1 for the char, and 3 for added padding, and 4 more for the int. As we
said, the padding is to align the memory access for the int member.
The Member Initializer List
Remind the students of the alternate initialization usage they saw first in Chapter 1, page
12 and 13.int count(0), limit(10);
double distance(5.723), zymurgyConstant(1.234);
This, for declaration of variables, is an exact equivalent to the following.
int count = 0, limit = 10;
double distance = 5.723, zymurgyConstant = 1.234;
When initializing data members in a class, it is possible to initialize inside the
constructor:
class A
{
public:
A();
// Other members
private:
int i;
int j;
};
A::A()
{
i = 0;
j = 1;
}
The preferred method is to use member initializer lists (also known as base class
initializer lists). Member initializer lists are only be used with the constructor definition,
not with the declaration within the class4.
4 If the definition of the constructor is placed in the class, inlined, then the initializer list is placed in the class, but this is not the usual practice in this book.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 7Chapter 14 Inheritance
class A
{
public:
A();
// Other members
private:
int i;
int j;
};
A::A() : i(0), j(0)
{// body deliberately empty
}
Stroustrup says that member initializers are required in situations where the semantics of
initialization differs from the semantics of assignment. That is, member initializers must
be used for members that are objects of classes not having default constructors, for
const members, and for reference members.
In the C++ FAQs, page 302 ff. Cline and Lomow say,
"Initialization lists are usually a performance issue, although there are cases when
the initialization lists impact correctness. . . . As a general rule, all member
objects and base classes should explicitly appear in the initialization list of a
constructor. In addition to being more efficient than to use the default
initialization followed by assignment, using the initialization list makes the code
clearer. There is no gain for built-in types, but there is no loss either, so
initialization lists should be used for symmetry.
…
Nonstatic const data members are declared in the class body with a const prefix
and … must be initialized in the constructor’s initialization list. …[A reference
data member] must be initialized in the initialization list of each constructor. "
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 8Chapter 14 Inheritance
Constructors In Derived Classes
Suppose for purposes of this section that class D is derived with specifier public
from class B. (All our inheritance will be public, I will refrain from mentioning that
the derivation is public in what follows.)
An object of the derived class D has both pieces from class D and inherited pieces
from base class B. As such, a derived class object’s inherited pieces constitute a slice
that is a purely base class object. If you take these inherited pieces, and add the pieces
added in the inheritance, you get the whole derived class object. In this sense a derived
class object has more than one type, namely, its own type and its base class type.
The derived class constructor can only initialize the variables defined in the derived class
definition. Only base class constructor can initialize base class variables. Constructors are
not inherited, and there is no way to call a base class constructor from within the body of
a derived class constructor. How, then, can the base class pieces be initialized? If we do
not somehow call an appropriate constructor, then the base class default constructor will
be called.
The default constructor may not produce the results we want. If we want some other
constructor to initialize the base class parts of our derived class, we call the desired base
class constructor in the member initializer list, before the variables are initialized.
class B
{
public:
B();
// other members
private:
int b
};
B::B() : b()
{// implementation
}
class D : public B
{
public:
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 9Chapter 14 Inheritance
D();
// other members
private:
int d;
};
D::D() : B(), d(0)
{// other implementation
}
The initialization is done by the implementation, not in the constructor declaration in the
class definition.
The protected Qualifier
We have seen that a member of a class can be public, or private. We have seen that
If the access qualifier is public, then any function can call a function in this
section, and any function can read or write a data member in this section.
If the access qualifier is private, then only functions declared in the class (or
friends of this class) can call a function in this section. Global functions and members
of nonfriend classes cannot call private functions read or write private data
members.
To these, C++ adds the keyword protected.
A member in a protected section can be accessed by member and friend functions
of this class and by members and friends of any derived class. The text puts it in
this succinct way: “Except for derived classes (and derived classes of derived
classes), a member variable that is marked protected is treated the same as if it were
private.
Members defined in the protected section of a class are accessible in member and
friend functions of the class, and in member and friend functions of classes derived
from the class. These members are inaccessible in any other function.
There are those who object to the use of friend functions on the theoretical ground that
it “breaks the encapsulation”. Stroustrup is blunt in his remarks regarding this attitude
toward friend functions, but he is not kind to the protected qualifier.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 10Chapter 14 Inheritance
Stroustrup remarks on the friend function concept on page 53 and following in The
Design and Evolution of C++, 1994, Addison Wesley.
“A friendship declaration was seen as a mechanism similar to that of one
protection domain granting read-write capability to another. It is an explicit and
specific part of a class declaration. Consequently, I have never been able to see
the recurring assertion that a friend declaration “violates encapsulation” as
anything but a combination ignorance and confusion with non-C++ terminology.”
Stroustrup is not so kind to the protected qualifier. In The Design and Evolution
of C++, page 301 and following, he says,
“Mark Linton was the main architect of Interviews [an X Windows toolkit]. … He
argued persuasively … that protected data was essential for the design of an
efficient and extensible … [X Windows] toolkit. …
“Five years later, Mark banned the use of protected data members in Interviews
because they had become a source of bugs. ‘… novice users poking where they
shouldn’t in ways they ought to have known better…’ They also seriously
complicate maintenance. …”
“In my experience there have always been alternatives to placing significant
amounts of information in a common base class for derived classes to use
directly. In fact, one of my concerns about protected is that it makes it too
easy to sue a common base the way one might sloppily have used global data.
“Fortunately, you don’t have to use protected data in C++; private is the
default for classes and is usually the better choice. Note that none of these
objections are significant for protected member functions. Use of protected
functions is a fine way of specifying operation for use in derived classes. …
“In retrospect, I think that protected is a case where ‘good arguments’ and
fashion overcame my better judgment … for accepting new features.”
My conclusion is that protected data members can be avoided so should not be used
but there is no problem with protected member functions or friend functions. I
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 11Chapter 14 Inheritance
conclude further that accessor and mutator functions intended to serve derived classes can
(and perhaps should) be protected member functions. If declared inline, then with
simple accessor/mutator functions have the efficiency of public access and the protection
of the C++ model.
Redefinition of Member Functions
If in a derived class a function appears with the exact same signature5 as a function in the
base class, then we say that the base class function has been redefined. A base class
object will call the base class definition and a derived class object will call the derived
class definition. 6
Redefinition Versus Overloading
Redefined functions have definitions with the same signatures. This is function
redefinition even if the one of the definitions is in a base class and the other definition is
in a class derived from the base class. It is quite common to refer to function redefinition
as hiding the base member. Stroustrup says “A name in a derived class hides any object
or function of the same name in a base class.
Overloaded functions have the same name but different same signatures. This is
function name overloading even if one of the definitions is in a base class and the other is
in a derived class.
Here is some sample code:
class B
{
public:
void f(arglist1){ /*implementation 1*/}5 The signature includes all information that participates in overload resolution: the types of the parameters, and, if the function is a class member, the signature includes constant or volatile qualification and the class of which the function is a member. See the ANSI C++ Standard, Chapter 1, page 2, section 1.3.10.6 Which definition is called when the member function is called through a pointer or a reference depends on the type of the pointer or reference, not on the object to which the pointer points or to which the reference refers. Modifying this behavior is a subject we study in the next chapter.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 12Chapter 14 Inheritance
void g(arglist2){ /*implementation 2*/}
};
class D : public B
{
void f(arglist1){ /*implementation 3 */} //Redefined
void g(arglist3){ /*implementation 4*/} //overloaded
};
The function f(arglist1) has one definition in the base class B and is redefined in
derived class D. The functions with name g are overloaded because they have different
signatures in base class B and in derived class D.
Access to a Redefined Function
Consider the code in the last section. Suppose we have defined a derived class D object
and call the function f through the object d.
D d;
d.f(arglist1);
The function that is called is the one associated with class D. Now suppose we want to
call the function associated with the base class function. We can do that with the scope
resolution operator, qualifying the function using the base class name:
d.B::f(arglist1);
14.2 Programming with Inheritance
The text presents some subtle detail regarding inheritance, another example, and some
discussion of programming techniques.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 13Chapter 14 Inheritance
Assignment Operators and Copy Constructors in Derived Classes.
Remember that an object of a derived class has two types. It has the slice that is the base
class object and the added data members. If we define an assignment operator, we must
somehow assign the base class slice and then assign the parts added by inheritance. The
base class parts are assigned using the base class operator=. The text’s example
assumes that class Derived inherits from class Base.
Derived& Derived::operator=(const Derived& rhs)
{
this->Base::operator=(rhs);
//other code . . .
}
This code invokes the base class overloaded assignment operator on the base class slice
of the calling object. The “other code” part then finishes the job.
Similarly, the copy constructor in the derived class must copy the base class slice, then
copy the part added in the definition of Derived. The code looks like this.
Derived::Derived(const Derived& rhs):Base(rhs) //other needed
{ //initialization
// other code needed to finish the copy
}
As in the derived class assignment operator, the call to the base class copy constructor
copies the base class slice of the derived class object. Then the rest of the initializations
serve to copy the Derived class parts added in the derivation.
Destructors in Derived Classes
A caveat is needed. The situation described in the text works fine as long as no destructor
is ever called through a pointer or a reference. That situation requires a virtual base class
destructor, and is discussed in the next chapter.
Suppose B is a base class, D is derived from B and D2 is derived from D. Then when an
object of type D goes out of scope, the destructors for these three classes are called
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 14Chapter 14 Inheritance
starting with ~D2, then ~D, then ~B. Said differently, the destructors are called in the
reverse order of the calls to constructors.
Protected and Private Inheritance; Multiple Inheritance
No remarks provided here.
3. Solutions to, and remarks on, selected Programming Projects
1. Administrator
Derive an Administrator class from SalariedEmployee in Display 14.4. It is
allowed (actually, it is recommended) to change the protection qualifier private to
protected in the base class.
Supply the following additional data and function members.
A variable of type string named title to contain the administrator's title.
Examples: Director, Vice President
A variable of type string named responsibility to hold the name of the area
of responsibility of the administrator. Example: Finance, Accounting, and Personnel.
A variable of type string named supervisor to hold the name of the
administrator's immediate supervisor.
A protected member variable of type double to hold the administrator's annual
salary.
A member function, setSupervisor, to change the name of the supervisor.
A member function to allow keyboard entry of the administrator's data.
A member function, print, that does screen display of administrator's data.
An overloaded member function PrintCheck() to print the administrator's data on
the check.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 15Chapter 14 Inheritance
Remarks: The function SalariedEmployee::PrintCheck() was tweaked. The
version of getline that comes with <string> in VC++6.0 has a bug. It seems to
lose something, so I used the cin member function getline instead of the version that
works with string objects directly.
//This is the HEADER FILE employee.h.
//This is the INTERFACE for the class Employee.
//This is primarily intended to be used as a base class to derive
//classes for different kinds of employees.
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include <string>
using std::string;
namespace SavitchEmployees
{
class Employee
{
public:
Employee( );
Employee(string theName, string theSsn);
string getName( );
string getSsn( );
double getNetPay( );
void setName(string newName);
void setSsn(string newSsn);
void setNetPay(double newNetPay);
void printCheck( );
protected:
string name;
string ssn;
double netPay;
};
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 16Chapter 14 Inheritance
}//SavitchEmployees
#endif //EMPLOYEE_H
//*****************************************************
//This is the IMPLEMENTATION FILE: employee.cpp
//This is the IMPLEMENTATION for the class Employee.
//The interface for the class Employee is in the header file employee.h.
#include <string>
#include <cstdlib>
#include <iostream>
#include "employee.h"
using std::string;
using std::cout;
namespace SavitchEmployees
{
Employee::Employee( ) : name("No name yet"),
ssn("No number yet"),
netPay(0)
{
//deliberately empty
}
0
Employee::Employee(string theName, string theNumber)
: name(theName),
ssn(theNumber),
netPay(0)
{
//deliberately empty
}
string Employee::getName( )
{
return name;
}
string Employee::getSsn( )
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 17Chapter 14 Inheritance
{
return ssn;
}
double Employee::getNetPay( )
{
return netPay;
}
void Employee::setName(string newName)
{
name = newName;
}
void Employee::setSsn(string newSsn)
{
ssn = newSsn;
}
void Employee::setNetPay (double newNetPay)
{
netPay = newNetPay;
}
void Employee::printCheck( )
{
cout << "\nERROR: printCheck FUNCTION CALLED FOR AN \n"
<< "UNDIFFERENTIATED EMPLOYEE. Aborting the program.\n"
<< "Check with the author of the program about this bug.\n";
exit(1);
}
}//SavitchEmployees
//*********************************************
//This is the HEADER FILE salariedemployee.h.
//This is the INTERFACE for the class SalariedEmployee.
#ifndef SALARIEDEMPLOYEE_H
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 18Chapter 14 Inheritance
#define SALARIEDEMPLOYEE_H
#include <string>
#include "employee.h"
using std::string;
namespace SavitchEmployees
{
class SalariedEmployee : public Employee
{
public:
SalariedEmployee( );
SalariedEmployee (string theName, string theSsn,
double theWeeklySalary);
double getSalary();
void setSalary(double newSalary);
void printCheck( );
private:
double salary; //weekly
};
}//SavitchEmployees
#endif //SALARIEDEMPLOYEE_H
//******************************************************
//This is the IMPLEMENTATION FILE: salariedemployee.cpp
//This is the IMPLEMENTATION for the class SalariedEmployee.
//The interface for the class SalariedEmployee is in
//the header file salariedemployee.h.
#include <iostream>
#include <string>
#include "salariedemployee.h"
using std::string;
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 19Chapter 14 Inheritance
using std::cout;
using std::endl;
namespace SavitchEmployees
{
SalariedEmployee::SalariedEmployee( ):Employee( ), salary(0)
{
//deliberately empty
}
SalariedEmployee::SalariedEmployee(string newName,
string newNumber,
double newWeeklyPay)
: Employee(newName, newNumber),
salary(newWeeklyPay)
{
//deliberately empty
}
double SalariedEmployee::getSalary( )
{
return salary;
}
void SalariedEmployee::setSalary(double newSalary)
{
salary = newSalary;
}
void SalariedEmployee::printCheck( )
{
setNetPay(salary);
cout << "\n_______________________________________________\n";
cout << "Pay to the order of " << getName( ) << endl;
cout << "The sum of " << getNetPay( ) << " Dollars\n";
cout << "_________________________________________________\n";
cout << "Check Stub NOT NEGOTIABLE \n";
cout << "Employee Number: " << getSsn( ) << endl;
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 20Chapter 14 Inheritance
cout << "Salaried Employee. Regular Pay: "
<< salary << endl;
cout << "_________________________________________________\n";
}
}//SavitchEmployees
//*********************************************************
//file: ch14p1.h
//This is the INTERFACE for class Administrator that inherits
//from class Employee and class SalariedEmployee
//Chapter 14 Programming Problem #1
//Title: Derive class Administrator
//
//Write a program using the class SalariedEmployee from
//Display 14.4. Define class Administrator that is derived
//publicly from class SalariedEmployee.
//Explain why there is a need to change private to protected.
//Answer: To allow the member functions inherited class member
//to see these data members.
//Additional Members required are:
//protected:
//string title; // administrator's title (CEO, Director etc.)
//string responsibility; // production, accounting, personnel
//string supervisor; //name of immediate supervisor
//double annualSalary;
//public:
//function to change name of immediate supervisor
//void changeSupervisor(string newSuper);
//function for reading all of a new administrator's data from
//the keyboard. Note that the data inherited from Employee will
//already be fetched automatically by default constructors.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 21Chapter 14 Inheritance
//void getAdminData();
// function to output administrator's data to the screen
// void print();
//prints a check with appropriate notations on the check
//void printCheck()
#ifndef PROTECT_ADMINISTRATOR_H
#define PROTECT_ADMINISTRATOR_H
#include "salariedemployee.h" // from Display 14.4
namespace SavitchEmployees
{
class Administrator : public SalariedEmployee
{
public:
//Constructors:
Administrator();
Administrator(string aName,
string anSSn,
string aTitle,
string aResponsibilty,
string aSupervisor,
double aSalary
);
// Administrator(string aTitle, string aResponsibilty,
// string aSupervisor, double aSalary);
// change name of immediate supervisor
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 22Chapter 14 Inheritance
void changeSupervisor(string newSuper);
// Reads a new administrator's data from the keyboard.
void getAdminData();
//outputs administrator's data to the screen
void print();
// prints a check with appropriate notations on the check
void printCheck();
void giveRaise(double amount);
protected:
string title; // administrator's title
// (CEO, Director, Vice President)
string responsibility; // such as production,
// accounting, personnel
string supervisor; // name of immediate supervisor
double annualSalary; // if not added in the self test exercise
};
} // end this segment of namespace SavitchEmployees
// #endif
//*****************************************************
//File: ch14p1.cpp
//Chapter 14 Programming Project 1
//Implementation for the Administrator derived class
#include "employee.h" //display 14.1
#include "ch14p1.h"
#include <iostream>
namespace SavitchEmployees
{
void Administrator::
giveRaise(double amount)
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 23Chapter 14 Inheritance
{
annualSalary += amount;
}
Administrator::
Administrator() : SalariedEmployee()
{
getAdminData();
}
Administrator::
Administrator(string aName,
string anSSn,
string aTitle,
string aResponsibilty,
string aSupervisor,
double aSalary
)
: SalariedEmployee(aName, anSSn, 0.0),
title(aTitle),
responsibility(aResponsibilty),
supervisor( aSupervisor), annualSalary(aSalary)
{
//deliberately empty
}
// change name of immediate supervisor
void Administrator::changeSupervisor(string newSuper)
{
supervisor = newSuper;
}
// Reading all of a new administrator's data from the keyboard.
void Administrator::getAdminData()
{
using namespace std;
char n[80];
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 24Chapter 14 Inheritance
cout << "\nAdministrative Employee data is being entered:\n\n";
cout << "Enter the Administrator's name: \n";
// getline(cin, name, '\n'); //there is a bug in VC++ version of
getline.
//getline(cin, name);
cin.getline(n, 80); //But this version works.
name = n;
cout << "Enter social security number: \n";
// getline(cin. ssn);
cin.getline(n, 80);
ssn = n;
cout << "Enter a title, followed by return:\n";
//getline(cin, title);
cin.getline(n, 80);
title = n;
cout << "\nEnter an area of responsibility followed by return:\n";
//getline(cin, responsibility);
cin.getline(n, 80);
responsibility = n;
cout << "\nEnter the name of employee's supervisor, followed "
<< "by return:\n";
//getline(cin, supervisor);
cin.getline(n, 80);
supervisor = n;
cout << "\nEnter salary followed by return:\n";
cin >> annualSalary;
}
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 25Chapter 14 Inheritance
//outputs administrator's data to the screen
void Administrator::print()
{
using namespace std;
cout << "\n\nprinting Administrator data \n";
cout << "Name: " << name << endl;
cout << "Annual Salary: $" << annualSalary << endl;
cout << "Social Security Number: " << ssn << endl;
cout << "Title: ";
cout << title << endl;
cout << "Responsibility:";
cout << responsibility << endl;
cout << "supervisor: ";
cout << supervisor << endl;
cout.setf(ios::showpoint);
cout.setf(ios::fixed);
cout.precision(2);
cout << "Annual Salary: "
<< annualSalary << endl << endl;
}
// prints a check with appropriate notations on the check
void Administrator::printCheck()
{
using namespace std;
double monthlyPay = annualSalary/12;
cout << "\n________________________________________________\n\n";
cout << "Pay to the order of " << name << endl << endl;
cout.setf(ios::showpoint);
cout.setf(ios::fixed);
cout.precision(2);
cout << "The sum of $" << monthlyPay << " Dollars\n";
cout << "\n_________________________________________________\n\n";
cout << "Check Stub NOT NEGOTIABLE \n";
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 26Chapter 14 Inheritance
cout << "Employee Number: " << ssn << endl << endl;
cout << "Administrative Employee. \n";
cout << "_________________________________________________\n";
}
}// end this segment namespace SavitchEmployees
No sample run is provided.
2. Additional Classes for Employee Hierarchy
Add temporary, administrative, permanent classification to hierarchy from Display 14.1,
14.3, and 14.4. Implement and test, test all member functions. A user interface with menu
would be a nice touch for testing.
Suggestions:
Once the details of #1 have been worked out, this problem seems (to me, at leas) to be
fairly straightforward. Nevertheless, I'm making some suggestions here, because my
students have difficulty in deciding which class to derive from a collection of already
defined class, and they have difficulty deciding what features to add in a derived class.
Derive class TemporaryEmployee from HourlyEmployee. Add string data
members hireDate and employmentTerm. Add access members and appropriate
constructors.
Derive the class PermanentEmployee from class SalariedEmployee. Add a
string data member hireDate data member and member function string
getHireDate();. Add constructors that reflect these changes.
Derive class AdministrativeEmployee from PermenantEmployee, adding a
string title data member and add a member function string getTitle();.
Add constructors that reflect these changes.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 27Chapter 14 Inheritance
Consider adding a string hireDate data member and string
setHireDate(); member function to the class hierarchy where not present.
Consider adding a class ContractEmployee. These employees are not really
temporary, as they are employed for the life of a contract (or less). They are likely to be
hourly employees and receive no fringe benefits, but the hourly pay rate is likely to be
quite high.
Consider adding a class Professional. A lawyer, physician, nurse practitioner or
physician assistant, or nurse might be a job that such an employee might do.
3. Doctor
Derive class Doctor from SalariedEmployee Display 14.4. A Doctor record has
the specialty (Pediatrician, Obstetrician, Surgeon, General Practitioner. – use string ),
office visit fee (double). Use reasonable constructors and accessor. Overload the
assignment operator, and provide a copy constructor. Write a driver to test everything.
This is also very much like #1 and #2. The difference is that there is only one class with a
specialty variable instead of providing separate classes. Here the member function
collection is the same for all specialties, whereas in #1 and #2, there are differences in the
interface for different employee types., which justified the more extensive use of
inheritance.
4. Person, Vehicle, Truck
Implementation of the class definition for Person is given. The student is to implement
the members, write the classes Vehicle and Truck, deriving class Truck from class
Vehicle,
Class Vehicle has a string variable containing the manufacturer's name, an int
variable containing the number of cylinders, and a Person variable containing the name
of the owner.
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 28Chapter 14 Inheritance
Class Truck is derived from Vehicle and has a double variable that contains load
capacity in tons, and an int variable containing the maximum towing capacity in
pounds.
Class Person is provided in the problem statement, and is repeated in the code.
Provide a reasonable complement of constructors, accessor, and overloaded assignment
operators. Write a driver that tests all methods.
NOTE: The default copy constructor and operator assignment do exactly what is needed.
I tested my code without writing these. The code works correctly with the default copy
constructor and operator assignment. Nevertheless, the exercise says to code these, so I
coded them. I (re)learned a bit doing this. I urge that the student be required to code these
per the problem statement.
//File: ch14p3.cpp
//Monolithic program
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person();
Person(const Person&);
Person& operator=(const Person& rhs);
Person(string);
Person::Person(const char* aName);
string getName() const;
friend istream& operator>> (istream& is, Person& p );
friend ostream& operator<< (ostream& os, const Person& p);
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 29Chapter 14 Inheritance
private:
string name;
};
class Vehicle
{
public:
Vehicle();
Vehicle(string mfgr, int noCyl, const Person& owner);
Vehicle(const Vehicle& rhs);
Vehicle& operator=(const Vehicle& rhs);
string getMfgrName();
int getNumberCyl();
Person getOwner();
private:
string mfgrName;
int numberCyl;
Person vehicleOwner;
};
class Truck : public Vehicle
{
public:
Truck();
Truck (double maxLoad, int maxTow,
string mfgr, int noCyl,
const Person& owner);
Truck(const Truck& rhs);
Truck& operator=(const Truck& rhs);
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 30Chapter 14 Inheritance
double getLoadCapacity();
int getTowingCapacity();
private:
double loadCapacity; // tons
int towingCapacity; // pounds
};
//*******************************************
//Vehicle implementation
Vehicle::Vehicle() : mfgrName("No Mfgr Yet"),
numberCyl(0),
vehicleOwner("No owner yet")
{//deliberately empty
}
Vehicle::Vehicle(string mfgr, int noCyl, const Person& owner)
: mfgrName(mfgr),
numberCyl(noCyl),
vehicleOwner(owner)
{//deliberately empty
}
Vehicle::Vehicle(const Vehicle& rhs)
: mfgrName(rhs.mfgrName),
numberCyl(rhs.numberCyl),
vehicleOwner(rhs.vehicleOwner)
{
}
Vehicle& Vehicle::operator=(const Vehicle& rhs)
{
this->mfgrName = rhs.mfgrName;
this->numberCyl = rhs.numberCyl;
this->vehicleOwner = rhs.vehicleOwner;
return *this;
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 31Chapter 14 Inheritance
}
string Vehicle::getMfgrName()
{
return mfgrName;
}
int Vehicle::getNumberCyl()
{
return numberCyl;
}
Person Vehicle::getOwner()
{
return vehicleOwner;
}
//**************************
// Truck Implementation
Truck::Truck() : loadCapacity(0), towingCapacity(0)
{
}
Truck::Truck(double maxLoad,
int maxTow,
string mfgr,
int noCyl,
const Person& owner)
: Vehicle(mfgr, noCyl, owner),
loadCapacity(maxLoad),
towingCapacity(maxTow)
{
}
Truck::Truck(const Truck& rhs)
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 32Chapter 14 Inheritance
: Vehicle(rhs),
loadCapacity(rhs.loadCapacity),
towingCapacity(rhs.towingCapacity)
{
}
Truck& Truck::operator=(const Truck& rhs)
{
Vehicle::operator=(rhs);
loadCapacity = rhs.loadCapacity;
towingCapacity = rhs.towingCapacity;
return *this;
}
double Truck::getLoadCapacity()
{
return loadCapacity;
}
int Truck::getTowingCapacity()
{
return towingCapacity;
}
//Person Implemenation
Person::Person() : name("")
{
}
Person::Person(string aName) : name(aName)
{
}
Person::Person(const char* aName) : name(aName)
{
}
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 33Chapter 14 Inheritance
Person::Person(const Person& theName):name(theName.name)
{
}
Person& Person::operator=(const Person& rhs)
{
this->name = rhs.name; //string assignment is well behaved
return *this;
}
string Person::getName() const
{
return name;
}
istream& operator>>(istream& is, Person& p )
{
is >> p.name;
return is;
}
ostream& operator<< (ostream& os, const Person& p)
{
os << p.name;
return os;
}
//********************
//Testing Application
int main()
{
Vehicle v1("Ford", 4, "James Carter") ;
Vehicle v2;
Vehicle v3(v1);
v2 = v1;
cout << "\nCar v1 (constructed) Data:\n";
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 34Chapter 14 Inheritance
cout << v1.getMfgrName() << endl;
cout << v1.getNumberCyl() << endl;
cout << v1.getOwner() << endl;
cout << "\nCar v2 (assigned) Data:\n";
cout << v2.getMfgrName() << endl;
cout << v2.getNumberCyl() << endl;
cout << v2.getOwner() << endl;
cout << "\nCar v3 (copy constructed) Data:\n";
cout << v3.getMfgrName() << endl;
cout << v3.getNumberCyl() << endl;
cout << v3.getOwner() << endl;
Truck t1(80.0, 20000, "Mac", 8, "John Q. Driver");
// 80 tons gross vehicle weight, 20,000 lbs tow capacity
Truck t3(t1), t2;
t2 = t1;
cout << "\nTruck T1 (constructed) data:\n";
cout << t1.getMfgrName() << endl;
cout << t1.getNumberCyl() << endl;
cout << t1.getOwner() << endl;
cout << t1.getLoadCapacity() << endl;
cout << t1.getTowingCapacity() << endl;
cout << "\nTruck T2 (assigned) data:\n";
cout << t2.getMfgrName() << endl;
cout << t2.getNumberCyl() << endl;
cout << t2.getOwner() << endl;
cout << t2.getLoadCapacity() << endl;
cout << t2.getTowingCapacity() << endl;
Instructor’s Resource Manual for Savitch Absolute C++ 05/08/23 Page 35Chapter 14 Inheritance
cout << "\nTruck T3 (copy constructed) data:\n";
cout << t3.getMfgrName() << endl;
cout << t3.getNumberCyl() << endl;
cout << t3.getOwner() << endl;
cout << t3.getLoadCapacity() << endl;
cout << t3.getTowingCapacity() << endl;
return 0;
}
// end testing application
5. Patient and Billing
This extends #4. Write classes Patient and Billing. Patient is derived from class
Person. Class Patient object has the patient's name (through inheritance from class
Person, and adds primary physician of type Doctor (Project #3). Class Billing
contains a Patient object as member to contain the patient's name, a Doctor object,
and an amount due of type double.
Provide a reasonable complement of constructors and accessor, overloaded assignment,
and a copy constructor.7
This is only qualitatively different from other problems in this section.
7 The default copy constructor and operator assignment do exactly what we need. In the real world, these should not be written. I recommend pointing out to the student that apart from the valuable learning experience, these should not be written in where the defaults work correctly.