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

10 1.2K 0
Absolute C++ (4th Edition) part 62 potx

Đang tải... (xem toàn văn)

Thông tin tài liệu

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. . 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. 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. 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

Ngày đăng: 04/07/2014, 05:21

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan