11.3.3. Creating a CommissionEmployeeBasePlusCommissionEmployee Inheritance Hierarchy

Now we create and test a new BasePlusCommissionEmployee class (Figs. 11.1011.11) that derives from class CommissionEmployee (Figs. 11.411.5). In this example, a BasePlusCommissionEmployee object is a CommissionEmployee (because inheritance passes on the capabilities of class CommissionEmployee), but class BasePlusCommissionEmployee also has data member baseSalary (Fig. 11.10, line 22). The colon (:) in line 10 of the class definition indicates inheritance. Keyword public indicates the type of inheritance. As a derived class (formed with public inheritance), BasePlusCommissionEmployee inherits all the members of class CommissionEmployee, except for the constructor—each class provides its own constructors that are specific to the class. (Destructors, too, are not inherited.) Thus, the public services of BasePlusCommissionEmployee include its constructor (lines 13–14) and the public member functions inherited from class CommissionEmployeealthough we cannot see these inherited member functions in BasePlusCommissionEmployee’s source code, they’re nevertheless a part of derived class BasePlusCommissionEmployee. The derived class’s public services also include member functions setBaseSalary, getBaseSalary, earnings and print (lines 16–20).


 1   // Fig. 11.10: BasePlusCommissionEmployee.h
 2   // BasePlusCommissionEmployee class derived from class
 3   // CommissionEmployee.
 4   #ifndef BASEPLUS_H
 5   #define BASEPLUS_H
 6
 7   #include <string> // C++ standard string class
 8   #include "CommissionEmployee.h" // CommissionEmployee class declaration
 9
10   class BasePlusCommissionEmployee : public CommissionEmployee
11   {
12   public:
13      BasePlusCommissionEmployee( const std::string &, const std::string &,
14         const std::string &, double = 0.0, double = 0.0, double = 0.0 );
15
16      void setBaseSalary( double ); // set base salary
17      double getBaseSalary() const; // return base salary
18
19      double earnings() const; // calculate earnings
20      void print() const; // print BasePlusCommissionEmployee object
21   private:
22      double baseSalary; // base salary
23   }; // end class BasePlusCommissionEmployee
24
25   #endif


Fig. 11.10. BasePlusCommissionEmployee class definition indicating inheritance relationship with class CommissionEmployee.


 1   // Fig. 11.11: BasePlusCommissionEmployee.cpp
 2   // Class BasePlusCommissionEmployee member-function definitions.
 3   #include <iostream>
 4   #include <stdexcept>
 5   #include "BasePlusCommissionEmployee.h"
 6   using namespace std;
 7
 8   // constructor
 9   BasePlusCommissionEmployee::BasePlusCommissionEmployee(
10      const string &first, const string &last, const string &ssn,
11      double sales, double rate, double salary )
12      // explicitly call base-class constructor            
13      : CommissionEmployee( first, last, ssn, sales, rate )
14   {
15      setBaseSalary( salary ); // validate and store base salary
16   } // end BasePlusCommissionEmployee constructor
17
18   // set base salary
19   void BasePlusCommissionEmployee::setBaseSalary( double salary )
20   {
21      if ( salary >= 0.0 )
22         baseSalary = salary;
23      else
24         throw invalid_argument( "Salary must be >= 0.0" );
25   } // end function setBaseSalary
26
27   // return base salary
28   double BasePlusCommissionEmployee::getBaseSalary() const
29   {
30      return baseSalary;
31   } // end function getBaseSalary
32
33   // calculate earnings
34   double BasePlusCommissionEmployee::earnings() const
35   {
36      // derived class cannot access the base class's private data
37      return baseSalary + ( commissionRate * grossSales );        
38   } // end function earnings
39
40   // print BasePlusCommissionEmployee object
41   void BasePlusCommissionEmployee::print() const
42   {
43      // derived class cannot access the base class's private data          
44      cout << "base-salaried commission employee: " << firstName << ' '     
45         << lastName << " social security number: " << socialSecurityNumber
46         << " gross sales: " << grossSales                                 
47         << " commission rate: " << commissionRate                         
48         << " base salary: " << baseSalary;                                
49   } // end function print

Compilation Errors from the LLVM Compiler in Xcode 4.5


BasePlusCommissionEmployee.cpp:37:26:
   'commissionRate' is a private member of 'CommissionEmployee'
BasePlusCommissionEmployee.cpp:37:43:
   'grossSales' is a private member of 'CommissionEmployee'
BasePlusCommissionEmployee.cpp:44:53:
   'firstName' is a private member of 'CommissionEmployee'
BasePlusCommissionEmployee.cpp:45:10:
   'lastName' is a private member of 'CommissionEmployee'
BasePlusCommissionEmployee.cpp:45:54:
   'socialSecurityNumber' is a private member of 'CommissionEmployee'
BasePlusCommissionEmployee.cpp:46:31:
   'grossSales' is a private member of 'CommissionEmployee'
BasePlusCommissionEmployee.cpp:47:35:
   'commissionRate' is a private member of 'CommissionEmployee'


Fig. 11.11. BasePlusCommissionEmployee implementation file: private base-class data cannot be accessed from derived class.

Figure 11.11 shows BasePlusCommissionEmployee’s member-function implementations. The constructor (lines 9–16) introduces base-class initializer syntax (line 13), which uses a member initializer to pass arguments to the base-class (CommissionEmployee) constructor. C++ requires that a derived-class constructor call its base-class constructor to initialize the base-class data members that are inherited into the derived class. Line 13 does this by explicitly invoking the CommissionEmployee constructor by name, passing the constructor’s parameters first, last, ssn, sales and rate as arguments to initialize the base-class data members firstName, lastName, socialSecurityNumber, grossSales and commissionRate, respectively. If BasePlusCommissionEmployee’s constructor did not invoke class CommissionEmployee’s constructor explicitly, C++ would attempt to invoke class CommissionEmployee’s default constructor implicitly—but the class does not have such a constructor, so the compiler would issue an error. Recall from Chapter 3 that the compiler provides a default constructor with no parameters in any class that does not explicitly include a constructor. However, CommissionEmployee does explicitly include a constructor, so a default constructor is not provided.


Image Common Programming Error 11.1

When a derived-class constructor calls a base-class constructor, the arguments passed to the base-class constructor must be consistent with the number and types of parameters specified in one of the base-class constructors; otherwise, a compilation error occurs.



Image Performance Tip 11.1

In a derived-class constructor, invoking base-class constructors and initializing member objects explicitly in the member initializer list prevents duplicate initialization in which a default constructor is called, then data members are modified again in the derived-class constructor’s body.


Compilation Errors from Accessing Base-Class private Members

The compiler generates errors for line 37 of Fig. 11.11 because base class CommissionEmployee’s data members commissionRate and grossSales are private—derived class BasePlusCommissionEmployee’s member functions are not allowed to access base class CommissionEmployee’s private data. The compiler issues additional errors in lines 44–47 of BasePlusCommissionEmployee’s print member function for the same reason. As you can see, C++ rigidly enforces restrictions on accessing private data members, so that even a derived class (which is intimately related to its base class) cannot access the base class’s private data.

Preventing the Errors in BasePlusCommissionEmployee

We purposely included the erroneous code in Fig. 11.11 to emphasize that a derived class’s member functions cannot access its base class’s private data. The errors in BasePlusCommissionEmployee could have been prevented by using the get member functions inherited from class CommissionEmployee. For example, line 37 could have invoked getCommissionRate and getGrossSales to access CommissionEmployee’s private data members commissionRate and grossSales, respectively. Similarly, lines 44–47 could have used appropriate get member functions to retrieve the values of the base class’s data members. In the next example, we show how using protected data also allows us to avoid the errors encountered in this example.

Including the Base-Class Header in the Derived-Class Header with #include

Notice that we #include the base class’s header in the derived class’s header (line 8 of Fig. 11.10). This is necessary for three reasons. First, for the derived class to use the base class’s name in line 10, we must tell the compiler that the base class exists—the class definition in CommissionEmployee.h does exactly that.

The second reason is that the compiler uses a class definition to determine the size of an object of that class (as we discussed in Section 3.6). A client program that creates an object of a class #includes the class definition to enable the compiler to reserve the proper amount of memory for the object. When using inheritance, a derived-class object’s size depends on the data members declared explicitly in its class definition and the data members inherited from its direct and indirect base classes. Including the base class’s definition in line 8 allows the compiler to determine the memory requirements for the base class’s data members that become part of a derived-class object and thus contribute to the total size of the derived-class object.

The last reason for line 8 is to allow the compiler to determine whether the derived class uses the base class’s inherited members properly. For example, in the program of Figs. 11.1011.11, the compiler uses the base-class header to determine that the data members being accessed by the derived class are private in the base class. Since these are inaccessible to the derived class, the compiler generates errors. The compiler also uses the base class’s function prototypes to validate function calls made by the derived class to the inherited base-class functions.

Linking Process in an Inheritance Hierarchy

In Section 3.7, we discussed the linking process for creating an executable GradeBook application. In that example, you saw that the client’s object code was linked with the object code for class GradeBook, as well as the object code for any C++ Standard Library classes used in either the client code or in class GradeBook.

The linking process is similar for a program that uses classes in an inheritance hierarchy. The process requires the object code for all classes used in the program and the object code for the direct and indirect base classes of any derived classes used by the program. Suppose a client wants to create an application that uses class BasePlusCommissionEmployee, which is a derived class of CommissionEmployee (we’ll see an example of this in Section 11.3.4). When compiling the client application, the client’s object code must be linked with the object code for classes BasePlusCommissionEmployee and CommissionEmployee, because BasePlusCommissionEmployee inherits member functions from its base class CommissionEmployee. The code is also linked with the object code for any C++ Standard Library classes used in class CommissionEmployee, class BasePlusCommissionEmployee or the client code. This provides the program with access to the implementations of all of the functionality that the program may use.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.255.127