PFArrayDBak::PFArrayDBak : usedB0 { b = new double[capacity]; } ALTERNATE IMPLEMENTATION OF PFArrayDBak At first glance it may seem that we needed to make the member variables of the b
Trang 1Self-Test Exercises
Pitfall SAME OBJECT ON BOTH SIDES OF THE ASSIGNMENT OPERATOR
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];
}
ALTERNATE IMPLEMENTATION 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
Trang 2Consider 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 CLASS HAS ACCESS TO PRIVATE MEMBERS OF ALL OBJECTS OF THE CLASS
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
Trang 3Display 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)
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
Trang 4Tip “IS A” VERSUS “HAS 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
Trang 5Self-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 propro-tected 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
Trang 6Note 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)
(Can only be used by name in definitions of member functions and friends)
(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
Trang 7■ 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 SalariedSalaried-Employee It is inherited unchanged from the base class Employee
Chapter Summary
Trang 85.#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
Trang 98 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
Trang 104 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