Tải bản đầy đủ (.pdf) (10 trang)

Absolute C++ (4th Edition) part 62 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (132.62 KB, 10 trang )

Programming with Inheritance 617
Example
Self-Test Exercises
Pitfall
S
AME
O
BJECT

ON
B
OTH
S
IDES

OF

THE
A
SSIGNMENT
O
PERATOR
Whenever you overload an assignment operator, always make sure your definition works when
the same object occurs on both sides of the assignment operator. In most cases, you will need to
make this a special case with some code of its own. An example of this is given in the program-
ming example “Partially Filled Array with Backup.”
8. Suppose Child is a class derived from the class Parent and that the class Grandchild is a
class derived from the class
Child. This question is concerned with the constructors and
destructors for the three classes
Parent, Child, and Grandchild. When a constructor for


the class
Grandchild is invoked, what constructors are invoked and in what order? When
the destructor for the class
Grandchild is invoked, what destructors are invoked and in
what order?
9. Is the following alternative definition of the default constructor for the class
PFArrayDBak
(Displays 14.10 and 14.11) legal? (The invocation of the constructor from the base class
has been omitted.) Explain your answer.
PFArrayDBak::PFArrayDBak( ) : usedB(0)
{
b = new double[capacity];
}
A
LTERNATE
I
MPLEMENTATION

OF

PFArrayDBak
At first glance it may seem that we needed to make the member variables of the base class PFAr-
rayD
protected in order to give the definitions of the member functions for the derived class
PFArrayDBak. After all, many of the member functions manipulate the inherited member vari-
ables
a, used, and capacity. The implementation we gave in Display 14.11 does indeed refer to
a, used, and capacity by name, and so those particular definitions do depend on these mem-
ber variables being protected in the base class (as opposed to private). However, we have enough
accessor and mutator functions in the base class that with just a bit more thinking, we can rewrite

the implementation of the derived class
PFArrayDBak so that it works even if all the member
variables in the base class
PFArrayD are private (rather than protected).
Display 14.13 shows an alternate implementation for the class
PFArrayDBak that works fine even
if all the member variables in the base class are private instead of protected. The parts that differ
from our previous implementation are shaded. Most changes are obvious, but there are a few
points that merit notice.
618 Inheritance
Tip
Consider the member function backup. In our previous implementation (Display 14.11), we copied
the array entries from a to b. Since a is now private, we cannot access it by name, but we have
overloaded the array square brackets operator (
operator[]) so that it applies to objects of type
PFArrayD, and this operator is inherited in PFArrayDBak. We simply use operator[] with the
calling object. The net effect is to copy from the array
a to the array b, but we never mention the
private array
a by name. The code is as follows:
usedB = getNumberUsed( );
for (int i = 0; i < usedB; i++)
b[i] = operator[](i);
Be sure to note the syntax for calling an operator of the class being defined. If superArray is an
object of the class
PFArrayDBak, then in the invocation superArray.backup( ), the notation
operator[](i) means superArray[i].
We could have used the notation
operator[](i) in our definition of the member function
restore, but it is just as easy to empty the array a with the inherited member function empty-

Array
and then add the backed-up elements using addElement. This way, we also set the pri-
vate member variable
used in the process.
With this alternate implementation, the class
PFArrayDBak is used just as it was with the previ-
ous implementation. In particular, the demonstration program in Display 14.12 works exactly the
same for either implementation.
A C
LASS
H
AS
A
CCESS

TO
P
RIVATE
M
EMBERS

OF
A
LL
O
BJECTS

OF

THE

C
LASS
Consider the following lines from the implementation of the overloaded assignment operator
given in Display 14.13:
usedB = rightSide.usedB;
for (int i = 0; i < usedB; i++)
b[i] = rightSide.b[i];
You might object that rightSide.usedB and rightSide.b[i] are illegal since usedB and b
are private member variables of some object other than the calling object. Normally that objec-
tion would be correct. However, the object
rightSide is of the same type as the class being
defined, and so this is legal.
In the definition of a class, you can access private members of any object of the class, not just pri-
vate members of the calling object.
Programming with Inheritance 619
Display 14.13 Alternate Implementation of PFArrayDBak
(part 1 of 2)
1 //This is the file pfarraydbak.cpp.
2 //This is the implementation of the class PFArrayDBak.
3 //The interface for the class PFArrayDBak is in the file pfarraydbak.h.
4 #include "pfarraydbak.h"
5 #include <iostream>
6 using std::cout;
7 PFArrayDBak::PFArrayDBak( ) : PFArrayD( ), usedB(0)
8 {
9 b = new double[getCapacity( )];
10 }
11 PFArrayDBak::PFArrayDBak(int capacityValue) : PFArrayD(capacityValue), usedB(0)
12 {
13 b = new double[getCapacity( )];

14 }
15 PFArrayDBak::PFArrayDBak(const PFArrayDBak& oldObject)
16 : PFArrayD(oldObject), usedB(0)
17 {
18 b = new double[getCapacity( )];
19 usedB = oldObject.usedB;
20 for (int i = 0; i < usedB; i++)
21 b[i] = oldObject.b[i];
22 }
23 void PFArrayDBak::backup( )
24 {
25 usedB = getNumberUsed( );
26 for (int i = 0; i < usedB; i++)
27 b[i] = operator[](i);
28 }
29
30 void PFArrayDBak::restore( )
31 {
32 emptyArray( );
33 for (int i = 0; i < usedB; i++)
34 addElement(b[i]);
35 }
36 PFArrayDBak& PFArrayDBak::operator =(const PFArrayDBak& rightSide)
37 {
38 PFArrayD::operator =(rightSide);
This implementation works even if all the
member variables in the base class are
private (rather than protected).
Invocation of the square brackets operator
with the calling object

620 Inheritance
Tip
“I
S

A

VERSUS
“H
AS

A

Early in this chapter we defined a derived class called HourlyEmployee using the class
Employee as the base class. In such a case an object of the derived class HourlyEmployee is
also of type
Employee. Stated more simply, an HourlyEmployee
is an
Employee. This is an
example of the “is a” relationship between classes. It is one way to make a more complex class
from a simpler class.
Another way to make a more complex class from a simpler class is known as the “has a” relation-
ship. For example, if you have a class
Date that records a date, then you might add a date of
employment to the
Employee class by adding a member variable of type Date to the Employee
class. In this case we say an
Employee “has a” Date. As another example, if we have an appopri-
ately named class to simulate a jet engine and we are defining a class to simulate a passenger air-
plane, then we can give the

PassengerAirPlane class one or more member variables of type
JetEngine. In this case we say that a PassengerAirPlane “has a” JetEngine.
In most situations you can make your code work with either an “is a” relationship or a “has a”
relationship. It seems silly (and it is silly) to make the PassengerAirPlane class a derived class
of the
JetEngine class, but it can be done and can be made to work (perhaps with difficulty).
Fortunately, the best programming technique is to simply follow what sounds most natural in
English. It makes more sense to say “A passenger airplane
has a jet engine” than it does to say “A
passenger airplane is a jet engine.” So, it makes better programming sense to have
JetEngine
as a member variable of a
PassengerAirPlane class. It makes little sense to make the Passen-
gerAirPlane
class a derived class of the JetEngine class.
Display 14.13 Alternate Implementation of PFArrayDBak
(part 2 of 2)
39 if (getCapacity( ) != rightSide.getCapacity( ))
40 {
41 delete [] b;
42 b = new double[rightSide.getCapacity( )];
43 }
44 usedB = rightSide.usedB;
45 for (int i = 0; i < usedB; i++)
46 b[i] = rightSide.b[i];
47 return *this;
48 }
49 PFArrayDBak::~PFArrayDBak( )
50 {
51 delete [] b;

52 }
“is a”
relation-
ship
“has a”
relation-
ship
Programming with Inheritance 621
Self-Test Exercises
10. Suppose you define a function with a parameter of type PFArrayD. Can you plug in an
object of the class
PFArrayDBak as an argument for this function?
11. Would the following be legal for the definition of a member function to add to the class
Employee (Display 14.1)? (Remember, the question is whether it is legal, not whether it is
sensible.)
void Employee::doStuff( )
{
Employee object("Joe", "123-45-6789");
cout << "Hello " << object.name << endl;
}

PROTECTED AND PRIVATE INHERITANCE
So far, all our definitions of derived classes included the keyword public in the class
heading, as in the following:
class SalariedEmployee : public Employee
{
This may lead you to suspect that the word public can be replaced with either pro-
tected
or private to obtain a different kind of inheritance. In this case, your suspicion
would be correct. However, protected and private inheritance are seldom used. We

include a brief description of them for the sake of completeness.
The syntax for protected and private inheritance is illustrated by the following:
class SalariedEmployee : protected Employee
{
If you use the keyword protected for inheritance, then members that are public in
the base class are protected in the derived class when they are inherited. If you use the
keyword
private for inheritance, then all members of the base class (public, protected,
and private) are inaccessible in the derived class; in other words, all members are inher-
ited as if they were marked
private in the base class.
Moreover, with protected and private inheritance, an object of the derived class can-
not be used as an argument that has the type of the base class. If
Derived is derived
from
Base using protected or private (instead of public), then an object of type
Derived is not an object of type Base; the “is a” relationship does not hold with pro-
tected and private inheritance. The idea is that with protected and private inheritance
the base class is simply a tool to use in defining the derived class. Although protected
and private inheritance can be made to work for some purposes, they are, as you might
suspect, seldom used, and any use they do have can be achieved in other ways.
The details about protected and private inheritance are summarized in Display 14.14.
622 Inheritance
Note that protected and private inheritance are not inheritance in the sense we
described for public inheritance. With protected or private inheritance the base class is
only a tool to be used in the derived class.

MULTIPLE INHERITANCE
It is possible for a derived class to have more than one base class. The syntax is very sim-
ple: All the base classes are listed, separated by commas. However, the possibilities for

ambiguity are numerous. What if two base classes have a function with the same name
and parameter types? Which is inherited? What if two base classes have a member vari-
able with the same name? All these questions can be answered, but these and other
problems make multiple inheritance a very dangerous business. Some authorities con-
sider multiple inheritance so dangerous that it should not be used at all. That may or
may not be too extreme a position, but it is true that you should not seriously attempt
multiple inheritance until you are a very experienced C++ programmer. At that point,
you will realize that you can almost always avoid multiple inheritance by using some
less dangerous technique. We will not discuss multiple inheritance in this book, but
leave it for more advanced references.
Display 14.14 Public, Protected, and Private Inheritance
Entries show how inherited members are treated in the derived class.
ACCESS SPECIFIER
IN BASE CLASS
TYPE OF INHERITANCE (SPECIFIER AFTER
CLASS NAME IN DERIVED CLASS DEFINITION)
public protected private
public
Public Protected Private
(Can only be used by
name in definitions of
member functions and
friends)
protected
Protected Protected Private
(Can only be used by
name in definitions of
member functions and
friends)
private

Cannot be accessed by
name in the derived
class
Cannot be accessed by
name in the derived
class
Cannot be accessed by
name in the derived
class
Answers to Self-Test Exercises 623
■ Inheritance provides a tool for code reuse by deriving one class from another, adding
features to the derived class.
■ Derived class objects inherit the members of the base class, and may add members.
■ If a member variable is private in a base class, then it cannot be accessed by name in
a derived class.
■ Private member functions are not inherited.
■ A member function may be redefined in a derived class so that it performs differ-
ently from how it performs in the base class. The declaration for a redefined member
function must be given in the class definition of the derived class, even though it is
the same as in the base class.
■ A protected member in the base class can be accessed by name in the definition of a
member function of a publicly derived class.
■ An overloaded assignment operator is not inherited. However, the assignment oper-
ator of a base class can be used in the definition of an overloaded assignment opera-
tor of a derived class.
■ Constructors are not inherited. However, a constructor of a base class can be used in
the definition of a constructor for a derived class.
ANSWERS TO SELF-TEST EXERCISES
1. Yes. You can plug in an object of a derived class for a parameter of the base class type.
An

HourlyEmployee is an Employee. A SalariedEmployee is an Employee.
2.
class SmartBut : public Smart
{
public:
SmartBut( );
SmartBut(int newA, int newB, bool newCrazy);
bool isCrazy( ) const;
private:
bool crazy;
};
3. It is legal because a and b are marked protected in the base class Smart and so they can
be accessed by name in a derived class. If
a and b had instead been marked private, then
this would be illegal.
4. The declaration for the function
getName is not given in the definition of Salaried-
Employee
because it is not redefined in the class SalariedEmployee. It is inherited
unchanged from the base class
Employee.
Chapter Summary
624 Inheritance
5. #include <iostream>
#include "salariedemployee.h"
using namespace std;
namespace SavitchEmployees
{
class TitledEmployee : public SalariedEmployee
{

public:
TitledEmployee( );
TitledEmployee(string theName, string theTitle,
string theSsn, double theSalary);
string getTitle( ) const;
void setTitle(string theTitle);
void setName(string theName);
private:
string title;
};
}//SavitchEmployees
6. namespace SavitchEmployees
{
TitledEmployee::TitledEmployee( )
: SalariedEmployee( ), title("No title yet")
{
//deliberately empty
}
TitledEmployee::TitledEmployee(string theName,
string theTitle,
string theSsn, double theSalary)
:
SalariedEmployee(theName, theSsn, theSalary),
title(theTitle)
{
//deliberately empty
}
void TitledEmployee::setName(string theName)
{
Employee::setName(title + theName);

}
}//SavitchEmployees
7. No. If you do not define an overloaded assignment operator or a copy constructor for a
derived class, then a default assignment operator and a default copy constructor will be
defined for the derived class. However, if the class involves pointers, dynamic arrays, or
other dynamic data, then it is almost certain that neither the default assignment operator
nor the default copy constructor will behave as you want them to.
Programming Projects 625
8. The constructors are called in the following order: first Parent, then Child, and finally
Grandchild. The destructors are called in the reverse order: first Grandchild, then
Child, and finally Parent.
9. Yes, it is legal and has the same meaning as the definition given in Display 14.11. If no base
class constructor is called, then the default constructor for the base class is called automati-
cally.
10. Yes. An object of a derived class is also an object of its base class. A
PFArrayDBak is a
PFArrayD.
11. Yes, it is legal. One reason you might think it illegal is that
name is a private member vari-
able. However,
object is in the class Employee, which is the class that is being defined, so
we have access to all member variables of all objects of the class
Employee.
PROGRAMMING PROJECTS
1. Write a program that uses the class SalariedEmployee given in Display 14.4. Your program
is to define a derived class called
Administrator, which is to be derived from the class Sala-
riedEmployee
. You are allowed to change private in the base class to protected. You are
to supply the following additional data and function members:

■ A member variable of type string that contains the administrator’s title, (such as Director
or Vice President).
■ A member variable of type string that contains the company area of responsibility (such
as Production, Accounting, or Personnel).
■ A member variable of type string that contains the name of this administrator’s immedi-
ate supervisor.
■ A protected member variable of type double that holds the administrator’s annual salary.
It is possible for you to use the existing salary member if you did the change recommended
above.
■ A member function called setSupervisor, which changes the supervisor name.
■ A member function for reading in an administrator’s data from the keyboard.
■ A member function called print, which outputs the object’s data to the screen.
■ Finally, an overloading of the member function printCheck( ) with appropriate notations
on the check.
2. Add temporary, administrative, permanent, and other classifications of employee to the
hierarchy from Displays 14.1, 14.3, and 14.4. Implement and test this hierarchy. Test all
member functions. A user interface with a menu would be a nice touch for your test
program.
3. Give the definition of a class named
Doctor whose objects are records for a clinic’s doctors.
This class will be a derived class of the class
SalariedEmployee given in Display 14.4. A
Doctor record has the doctor’s specialty (such as “Pediatrician,” “Obstetrician,” “General
Practitioner,” etc., so use type
string), and office visit fee (use type double). Be sure your
class has a reasonable complement of constructors and accessor methods, an overloaded
assignment operator, and a copy constructor. Write a driver program to test all your methods.
626 Inheritance
4. Create a base class called Vehicle that has the manufacturer’s name (type string), num-
ber of cylinder’s in the engine (type

int), and owner (type Person given below). Then cre-
ate a class called
Truck that is derived from Vehicle and has additional properties, the
load capacity in tons (type
double since it may contain a fractional part) and towing capac-
ity in pounds (type
int). Be sure your classes have a reasonable complement of construc-
tors and accessor methods, an overloaded assignment operator, and a copy constructor.
Write a driver program that tests all your methods.
The definition of the class
Person is below. The implementation of the class is part of this
programming project.
class Person
{
public:
Person();
Person(string theName);
Person(const Person& theObject);
string getName() const;
Person& operator=(const Person& rtSide);
friend istream& operator >>(istream& inStream,
Person& personObject);
friend ostream& operator <<(ostream& outStream,
const Person& personObject);
private:
string name;
};
5. Give the definition of two classes, Patient and Billing, whose objects are records for a
clinic.
Patient will be derived from the class Person given in Programming Project 4. A

Patient record has the patient’s name (inherited from the class Person) and primary phy-
sician, of type
Doctor defined in Programming Project 3. A Billing object will contain a
Patient object and a Doctor object, and an amount due of type double. Be sure your
classes have a reasonable complement of constructors and accessor methods, an overloaded
assignment operator, and a copy constructor. First write a driver program to test all your
methods, then write a test program that creates at least two patients, at least two doctors, at
least two
Billing records, then prints out the total income from the Billing records.
1.7
For additional online
Programming Projects,
click the CodeMate
icons below.

×