Relationship between Base Classes and Derived Classes

In this section, we use an inheritance hierarchy containing types of employees in a company's payroll application to discuss the relationship between a base class and a derived class. Commission employees (who will be represented as objects of a base class) are paid a percentage of their sales, while base-salaried commission employees (who will be represented as objects of a derived class) receive a base salary plus a percentage of their sales. We divide our discussion of the relationship between commission employees and base-salaried commission employees into a carefully paced series of five examples:


  1. In the first example, we create class CommissionEmployee, which contains as private data members a first name, last name, social security number, commissionrate (percentage) and gross (i.e., total) sales amount.
  2. The second example defines class BasePlusCommissionEmployee, which contains as private data members a first name, last name, social security number, commission rate, gross sales amount and base salary. We create the latter class by writing every line of code the class requireswe will soon see that it is much more efficient to create this class simply by inheriting from class CommissionEmployee.
  3. The third example defines a new version of class BasePlusCommissionEmployee class that inherits directly from class CommissionEmployee (i.e., a BasePlusCommissionEmployee is a CommissionEmployee who also has a base salary) and attempts to access class CommissionEmployee's private membersthis results in compilation errors, because the derived class does not have access to the base class's private data.
  4. The fourth example shows that if CommissionEmployee's data is declared as protected, a new version of class BasePlusCommissionEmployee that inherits from class CommissionEmployee can access that data directly. For this purpose, we define a new version of class CommissionEmployee with protected data. Both the inherited and noninherited BasePlusCommissionEmployee classes contain identical functionality, but we show how the version of BasePlusCommissionEmployee that inherits from class CommissionEmployee is easier to create and manage.
  5. After we discuss the convenience of using protected data, we create the fifth example, which sets the CommissionEmployee data members back to private to enforce good software engineering. This example demonstrates that derived class BasePlusCommissionEmployee can use base class CommissionEmployee's public member functions to manipulate CommissionEmployee's private data.

12.4.1. Creating and Using a CommissionEmployee Class

Let us first examine CommissionEmployee's class definition (Figs. 12.412.5). The CommissionEmployee header file (Fig. 12.4) specifies class CommissionEmployee's public services, which include a constructor (lines 1213) and member functions earnings (line 30) and print (line 31). Lines 1528 declare public get and set functions for manipulating the class's data members (declared in lines 3337) firstName, lastName, socialSecurityNumber, grossSales and commissionRate. The CommissionEmployee header file specifies each of these data members as private, so objects of other classes cannot directly access this data. Declaring data members as private and providing non-private get and set functions to manipulate and validate the data members helps enforce good software engineering. Member functions setGrossSales (defined in lines 5760 of Fig. 12.5) and setCommissionRate (defined in lines 6972 of Fig. 12.5), for example, validate their arguments before assigning the values to data members grossSales and commissionRate, respectively.

Figure 12.4. CommissionEmployee class header file.

(This item is displayed on page 640 in the print version)

 1 // Fig. 12.4: CommissionEmployee.h
 2 // CommissionEmployee class definition represents a commission employee.
 3 #ifndef COMMISSION_H
 4 #define COMMISSION_H
 5
 6 #include  // C++ standard string class
 7 using std::string;
 8
 9 class CommissionEmployee
10 {
11 public:
12 CommissionEmployee( const string &, const string &, const string &,
13  double = 0.0, double = 0.0 ); 
14
15 void setFirstName( const string & ); // set first name
16 string getFirstName() const; // return first name
17
18 void setLastName( const string & ); // set last name
19 string getLastName() const; // return last name
20
21 void setSocialSecurityNumber( const string & ); // set SSN
22 string getSocialSecurityNumber() const; // return SSN
23
24 void setGrossSales( double ); // set gross sales amount
25 double getGrossSales() const; // return gross sales amount
26
27 void setCommissionRate( double ); // set commission rate (percentage)
28 double getCommissionRate() const; // return commission rate
29
30 double earnings() const; // calculate earnings
31 void print() const; // print CommissionEmployee object
32 private:
33 string firstName; 
34 string lastName; 
35 string socialSecurityNumber; 
36 double grossSales; // gross weekly sales 
37 double commissionRate; // commission percentage
38 }; // end class CommissionEmployee
39
40 #endif

Figure 12.5. Implementation file for CommissionEmployee class that represents an employee who is paid a percentage of gross sales.

(This item is displayed on pages 640 - 642 in the print version)

 1 // Fig. 12.5: CommissionEmployee.cpp
 2 // Class CommissionEmployee member-function definitions.
 3 #include 
 4 using std::cout;
 5
 6 #include "CommissionEmployee.h" // CommissionEmployee class definition
 7
 8 // constructor 
 9 CommissionEmployee::CommissionEmployee( 
10  const string &first, const string &last, const string &ssn, 
11  double sales, double rate ) 
12 { 
13  firstName = first; // should validate 
14  lastName = last; // should validate 
15  socialSecurityNumber = ssn; // should validate 
16  setGrossSales( sales ); // validate and store gross sales 
17  setCommissionRate( rate ); // validate and store commission rate
18 } // end CommissionEmployee constructor 
19
20 // set first name
21 void CommissionEmployee::setFirstName( const string &first )
22 {
23 firstName = first; // should validate
24 } // end function setFirstName
25
26 // return first name
27 string CommissionEmployee::getFirstName() const
28 {
29 return firstName;
30 } // end function getFirstName
31
32 // set last name
33 void CommissionEmployee::setLastName( const string &last )
34 {
35 lastName = last; // should validate
36 } // end function setLastName
37
38 // return last name
39 string CommissionEmployee::getLastName() const
40 {
41 return lastName;
42 } // end function getLastName
43
44 // set social security number
45 void CommissionEmployee::setSocialSecurityNumber( const string &ssn )
46 {
47 socialSecurityNumber = ssn; // should validate
48 } // end function setSocialSecurityNumber
49
50 // return social security number
51 string CommissionEmployee::getSocialSecurityNumber() const
52 {
53 return socialSecurityNumber;
54 } // end function getSocialSecurityNumber
55
56 // set gross sales amount
57 void CommissionEmployee::setGrossSales( double sales )
58 {
59 grossSales = ( sales < 0.0 ) ? 0.0 : sales;
60 } // end function setGrossSales
61
62 // return gross sales amount
63 double CommissionEmployee::getGrossSales() const
64 {
65 return grossSales;
66 } // end function getGrossSales
67
68 // set commission rate
69 void CommissionEmployee::setCommissionRate( double rate )
70 {
71 commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0;
72 } // end function setCommissionRate
73
74 // return commission rate
75 double CommissionEmployee::getCommissionRate() const
76 {
77 return commissionRate;
78 } // end function getCommissionRate
79
80 // calculate earnings 
81 double CommissionEmployee::earnings() const
82 { 
83  return commissionRate * grossSales; 
84 } // end function earnings 
85
86 // print CommissionEmployee object 
87 void CommissionEmployee::print() const 
88 { 
89  cout << "commission employee: " << firstName << ' ' << lastName
90  << "
social security number: " << socialSecurityNumber 
91  << "
gross sales: " << grossSales 
92  << "
commission rate: " << commissionRate; 
93 } // end function print 

The CommissionEmployee constructor definition purposely does not use member-initializer syntax in the first several examples of this section, so that we can demonstrate how private and protected specifiers affect member access in derived classes. As shown in Fig. 12.5, lines 1315, we assign values to data members firstName, lastName and socialSecurityNumber in the constructor body. Later in this section, we will return to using member-initializer lists in the constructors.


Note that we do not validate the values of the constructor's arguments first, last and ssn before assigning them to the corresponding data members. We certainly could validate the first and last namesperhaps by ensuring that they are of a reasonable length. Similarly, a social security number could be validated to ensure that it contains nine digits, with or without dashes (e.g., 123-45-6789 or 123456789).

Member function earnings (lines 8184) calculates a CommissionEmployee's earnings. Line 83 multiplies the commissionRate by the grossSales and returns the result. Member function print (lines 8793) displays the values of a CommissionEmployee object's data members.

Figure 12.6 tests class CommissionEmployee. Lines 1617 instantiate object employee of class CommissionEmployee and invoke CommissionEmployee's constructor to initialize the object with "Sue" as the first name, "Jones" as the last name, "222-22-2222" as the social security number, 10000 as the gross sales amount and .06 as the commission rate. Lines 2329 use employee's get functions to display the values of its data members. Lines 3132 invoke the object's member functions setGrossSales and setCommissionRate to change the values of data members grossSales and commissionRate, respectively. Line 36 then calls employee's print member function to output the updated CommissionEmployee information. Finally, line 39 displays the CommissionEmployee's earnings, calculated by the object's earnings member function using the updated values of data members grossSales and commissionRate.


Figure 12.6. CommissionEmployee class test program.

(This item is displayed on pages 643 - 644 in the print version)

 1 // Fig. 12.6: fig12_06.cpp
 2 // Testing class CommissionEmployee.
 3 #include 
 4 using std::cout;
 5 using std::endl;
 6 using std::fixed;
 7
 8 #include 
 9 using std::setprecision;
10
11 #include "CommissionEmployee.h" // CommissionEmployee class definition
12
13 int main()
14 {
15 // instantiate a CommissionEmployee object 
16 CommissionEmployee employee( 
17  "Sue", "Jones", "222-22-2222", 10000, .06 );
18
19 // set floating-point output formatting
20 cout << fixed << setprecision( 2 );
21
22 // get commission employee data
23 cout << "Employee information obtained by get functions: 
"
24 << "
First name is " << employee.getFirstName() 
25 << "
Last name is " << employee.getLastName()
26 << "
Social security number is "
27 << employee.getSocialSecurityNumber()
28 << "
Gross sales is " << employee.getGrossSales()
29 << "
Commission rate is " << employee.getCommissionRate() << endl;
30
31 employee.setGrossSales( 8000 ); // set gross sales 
32 employee.setCommissionRate( .1 ); // set commission rate
33
34 cout << "
Updated employee information output by print function: 
"
35 << endl;
36 employee.print(); // display the new employee information
37
38 // display the employee's earnings
39 cout << "

Employee's earnings: $" << employee.earnings() << endl;
40
41 return 0;
42 } // end main
 
 Employee information obtained by get functions:

 First name is Sue
 Last name is Jones
 Social security number is 222-22-2222
 Gross sales is 10000.00
 Commission rate is 0.06

 Updated employee information output by print function:

 commission employee: Sue Jones
 social security number: 222-22-2222
 gross sales: 8000.00
 commission rate: 0.10

 Employee's earnings: $800.00
 

12.4.2. Creating a BasePlusCommissionEmployee Class Without Using Inheritance

We now discuss the second part of our introduction to inheritance by creating and testing (a completely new and independent) class BasePlusCommissionEmployee (Figs. 12.712.8), which contains a first name, last name, social security number, gross sales amount, commission rate and base salary.

Figure 12.7. BasePlusCommissionEmployee class header file.

(This item is displayed on pages 644 - 645 in the print version)

 1 // Fig. 12.7: BasePlusCommissionEmployee.h
 2 // BasePlusCommissionEmployee class definition represents an employee
 3 // that receives a base salary in addition to commission.
 4 #ifndef BASEPLUS_H
 5 #define BASEPLUS_H
 6
 7 #include  // C++ standard string class
 8 using std::string;
 9
10 class BasePlusCommissionEmployee
11 {
12 public:
13 BasePlusCommissionEmployee( const string &, const string &,
14 const string &, double = 0.0, double = 0.0, double = 0.0 );
15
16 void setFirstName( const string & ); // set first name
17 string getFirstName() const; // return first name
18
19 void setLastName( const string & ); // set last name
20 string getLastName() const; // return last name
21
22 void setSocialSecurityNumber( const string & ); // set SSN
23 string getSocialSecurityNumber() const; // return SSN
24
25 void setGrossSales( double ); // set gross sales amount
26 double getGrossSales() const; // return gross sales amount
27
28 void setCommissionRate( double ); // set commission rate
29 double getCommissionRate() const; // return commission rate
30
31 void setBaseSalary( double ); // set base salary 
32 double getBaseSalary() const; // return base salary
33
34 double earnings() const; // calculate earnings
35 void print() const; // print BasePlusCommissionEmployee object
36 private:
37 string firstName;
38 string lastName;
39 string socialSecurityNumber;
40 double grossSales; // gross weekly sales
41 double commissionRate; // commission percentage
42 double baseSalary; // base salary
43 }; // end class BasePlusCommissionEmployee
44
45 #endif

Figure 12.8. BasePlusCommissionEmployee class represents an employee who receives a base salary in addition to a commission.

(This item is displayed on pages 646 - 648 in the print version)

 1 // Fig. 12.8: BasePlusCommissionEmployee.cpp
 2 // Class BasePlusCommissionEmployee member-function definitions.
 3 #include 
 4 using std::cout;
 5
 6 // BasePlusCommissionEmployee class definition
 7 #include "BasePlusCommissionEmployee.h"
 8
 9 // constructor
10 BasePlusCommissionEmployee::BasePlusCommissionEmployee(
11 const string &first, const string &last, const string &ssn,
12 double sales, double rate, double salary )
13 {
14 firstName = first; // should validate
15 lastName = last; // should validate
16 socialSecurityNumber = ssn; // should validate
17 setGrossSales( sales ); // validate and store gross sales
18 setCommissionRate( rate ); // validate and store commission rate
19 setBaseSalary( salary ); // validate and store base salary
20 } // end BasePlusCommissionEmployee constructor
21
22 // set first name
23 void BasePlusCommissionEmployee::setFirstName( const string &first )
24 {
25 firstName = first; // should validate
26 } // end function setFirstName
27
28 // return first name
29 string BasePlusCommissionEmployee::getFirstName() const
30 {
31 return firstName;
32 } // end function getFirstName
33
34 // set last name
35 void BasePlusCommissionEmployee::setLastName( const string &last )
36 {
37 lastName = last; // should validate
38 } // end function setLastName
39
40 // return last name
41 string BasePlusCommissionEmployee::getLastName() const
42 {
43 return lastName;
44 } // end function getLastName
45
46 // set social security number
47 void BasePlusCommissionEmployee::setSocialSecurityNumber(
48 const string &ssn )
49 {
50 socialSecurityNumber = ssn; // should validate
51 } // end function setSocialSecurityNumber
52
53 // return social security number
54 string BasePlusCommissionEmployee::getSocialSecurityNumber() const
55 {
56 return socialSecurityNumber;
57 } // end function getSocialSecurityNumber
58
59 // set gross sales amount
60 void BasePlusCommissionEmployee::setGrossSales( double sales )
61 {
62 grossSales = ( sales < 0.0 ) ? 0.0 : sales;
63 } // end function setGrossSales
64
65 // return gross sales amount
66 double BasePlusCommissionEmployee::getGrossSales() const
67 {
68 return grossSales;
69 } // end function getGrossSales
70
71 // set commission rate
72 void BasePlusCommissionEmployee::setCommissionRate( double rate )
73 {
74 commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0;
75 } // end function setCommissionRate
76
77 // return commission rate
78 double BasePlusCommissionEmployee::getCommissionRate() const
79 {
80 return commissionRate;
81 } // end function getCommissionRate
82
83 // set base salary 
84 void BasePlusCommissionEmployee::setBaseSalary( double salary )
85 { 
86  baseSalary = ( salary < 0.0 ) ? 0.0 : salary; 
87 } // end function setBaseSalary 
88
89 // return base salary 
90 double BasePlusCommissionEmployee::getBaseSalary() const
91 { 
92  return baseSalary; 
93 } // end function getBaseSalary 
94
95 // calculate earnings 
96 double BasePlusCommissionEmployee::earnings() const 
97 { 
98  return baseSalary + ( commissionRate * grossSales );
99 } // end function earnings 
100
101 // print BasePlusCommissionEmployee object
102 void BasePlusCommissionEmployee::print() const
103 {
104 cout << "base-salaried commission employee: " << firstName << ' '
105 << lastName << "
social security number: " << socialSecurityNumber
106 << "
gross sales: " << grossSales
107 << "
commission rate: " << commissionRate
108 << "
base salary: " << baseSalary;
109 } // end function print

Defining Class BasePlusCommissionEmployee

The BasePlusCommissionEmployee header file (Fig. 12.7) specifies class BasePlusCommissionEmployee's public services, which include the BasePlusCommissionEmployee constructor (lines 1314) and member functions earnings (line 34) and print (line 35). Lines 1632 declare public get and set functions for the class's private data members (declared in lines 3742) firstName, lastName, socialSecurityNumber, grossSales, commissionRate and baseSalary. These variables and member functions encapsulate all the necessary features of a base-salaried commission employee. Note the similarity between this class and class CommissionEmployee (Figs. 12.412.5)in this example, we will not yet exploit that similarity.


Class BasePlusCommissionEmployee's earnings member function (defined in lines 9699 of Fig. 12.8) computes the earnings of a base-salaried commission employee. Line 98 returns the result of adding the employee's base salary to the product of the commission rate and the employee's gross sales.

Testing Class BasePlusCommissionEmployee

Figure 12.9 tests class BasePlusCommissionEmployee. Lines 1718 instantiate object employee of class BasePlusCommissionEmployee, passing "Bob", "Lewis", "333-33-3333", 5000, .04 and 300 to the constructor as the first name, last name, social security number, gross sales, commission rate and base salary, respectively. Lines 2431 use BasePlusCommissionEmployee's get functions to retrieve the values of the object's data members for output. Line 33 invokes the object's setBaseSalary member function to change the base salary. Member function setBaseSalary (Fig. 12.8, lines 8487) ensures that data member baseSalary is not assigned a negative value, because an employee's base salary cannot be negative. Line 37 of Fig. 12.9 invokes the object's print member function to output the updated BasePlusCommissionEmployee's information, and line 40 calls member function earnings to display the BasePlusCommissionEmployee's earnings.



Figure 12.9. BasePlusCommissionEmployee class test program.

(This item is displayed on pages 648 - 649 in the print version)

 1 // Fig. 12.9: fig12_09.cpp
 2 // Testing class BasePlusCommissionEmployee.
 3 #include 
 4 using std::cout;
 5 using std::endl;
 6 using std::fixed;
 7
 8 #include 
 9 using std::setprecision;
10
11 // BasePlusCommissionEmployee class definition
12 #include "BasePlusCommissionEmployee.h"
13
14 int main()
15 {
16 // instantiate BasePlusCommissionEmployee object
17 BasePlusCommissionEmployee
18 employee( "Bob", "Lewis", "333-33-3333", 5000, .04, 300 );
19
20 // set floating-point output formatting
21 cout << fixed << setprecision( 2 );
22
23 // get commission employee data
24 cout << "Employee information obtained by get functions: 
"
25 << "
First name is " << employee.getFirstName()
26 << "
Last name is " << employee.getLastName()
27 << "
Social security number is "
28 << employee.getSocialSecurityNumber()
29 << "
Gross sales is " << employee.getGrossSales()
30 << "
Commission rate is " << employee.getCommissionRate()
31 << "
Base salary is " << employee.getBaseSalary() << endl;
32
33 employee.setBaseSalary( 1000 ); // set base salary
34
35 cout << "
Updated employee information output by print function: 
"
36 << endl;
37 employee.print(); // display the new employee information
38
39 // display the employee's earnings
40 cout << "

Employee's earnings: $" << employee.earnings() << endl;
41
42 return 0;
43 } // end main
 
 Employee information obtained by get functions:

 First name is Bob
 Last name is Lewis
 Social security number is 333-33-3333
 Gross sales is 5000.00
 Commission rate is 0.04
 Base salary is 300.00

 Updated employee information output by print function:

 base-salaried commission employee: Bob Lewis
 social security number: 333-33-3333
 gross sales: 5000.00
 commission rate: 0.04
 base salary: 1000.00

 Employee's earnings: $1200.00
 

Exploring the Similarities Between Class BasePlusCommissionEmployee and Class CommissionEmployee

Note that much of the code for class BasePlusCommissionEmployee (Figs. 12.712.8) is similar, if not identical, to the code for class CommissionEmployee (Figs. 12.412.5). For example, in class BasePlusCommissionEmployee, private data members firstName and lastName and member functions setFirstName, getFirstName, setLastName and getLastName are identical to those of class CommissionEmployee. Classes CommissionEmployee and BasePlusCommissionEmployee also both contain private data members socialSecurityNumber, commissionRate and grossSales, as well as get and set functions to manipulate these members. In addition, the BasePlusCommissionEmployee constructor is almost identical to that of class CommissionEmployee, except that BasePlusCommissionEmployee's constructor also sets the baseSalary. The other additions to class BasePlusCommissionEmployee are private data member baseSalary and member functions setBaseSalary and getBaseSalary. Class BasePlusCommissionEmployee's print member function is nearly identical to that of class CommissionEmployee, except that BasePlusCommissionEmployee's print also outputs the value of data member baseSalary.

We literally copied code from class CommissionEmployee and pasted it into class BasePlusCommissionEmployee, then modified class BasePlusCommissionEmployee to include a base salary and member functions that manipulate the base salary. This "copy-and-paste" approach is often error prone and time consuming. Worse yet, it can spread many physical copies of the same code throughout a system, creating a code-maintenance nightmare. Is there a way to "absorb" the data members and member functions of a class in a way that makes them part of other classes without duplicating code? In the next several examples, we do exactly this, using inheritance.


Software Engineering Observation 12.3

Copying and pasting code from one class to another can spread errors across multiple source code files. To avoid duplicating code (and possibly errors), use inheritance, rather than the "copy-andpaste" approach, in situations where you want one class to "absorb" the data members and member functions of another class.

Software Engineering Observation 12.4

With inheritance, the common data members and member functions of all the classes in the hierarchy are declared in a base class. When changes are required for these common features, software developers need to make the changes only in the base classderived classes then inherit the changes. Without inheritance, changes would need to be made to all the source code files that contain a copy of the code in question.

 

12.4.3. Creating a CommissionEmployeeBasePlusCommissionEmployee Inheritance Hierarchy

Now we create and test a new version of class BasePlusCommissionEmployee (Figs. 12.1012.11) that derives from class CommissionEmployee (Figs. 12.412.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. 12.10, line 24). The colon (:) in line 12 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 constructoreach class provides its own constructors that are specific to the class. [Note that destructors, too, are not inherited.] Thus, the public services of BasePlusCommissionEmployee include its constructor (lines 1516) and the public member functions inherited from class CommissionEmployeealthough we cannot see these inherited member functions in BasePlusCommissionEmployee's source code, they are nevertheless a part of derived class BasePlusCommissionEmployee. The derived class's public services also include member functions setBaseSalary, getBaseSalary, earnings and print (lines 1822).


Figure 12.10. BasePlusCommissionEmployee class definition indicating inheritance relationship with class CommissionEmployee.

(This item is displayed on page 650 in the print version)

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

Figure 12.11. BasePlusCommissionEmployee implementation file: private base-class data cannot be accessed from derived class.

(This item is displayed on pages 651 - 653 in the print version)

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

 C:cpphtp5_examplesch12Fig12_10_11BasePlusCommission-Employee.cpp (35) :
 error C2248: 'CommissionEmployee::grossSales' :
 cannot access private member declared in class 'CommissionEmployee'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h (36) :
 see declaration of 'CommissionEmployee::grossSales'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(10) :
 see declaration of 'CommissionEmployee'

 C:cpphtp5_examplesch12Fig12_10_11BasePlusCommission-Employee.cpp(42) :
 error C2248: 'CommissionEmployee::firstName' :
 cannot access private member declared in class 'CommissionEmployee'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(33) :
 see declaration of 'CommissionEmployee::firstName'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(10) :
 see declaration of 'CommissionEmployee'

 C:cpphtp5_examplesch12Fig12_10_11BasePlusCommission-Employee.cpp(43) :
 error C2248: 'CommissionEmployee::lastName' :
 cannot access private member declared in class 'CommissionEmployee'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(34) :
 see declaration of 'CommissionEmployee::lastName'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(10) :
 see declaration of 'CommissionEmployee'

 C:cpphtp5_examplesch12Fig12_10_11BasePlusCommission-Employee.cpp(43) :
 error C2248: 'CommissionEmployee::socialSecurity-Number' :
 cannot access private member declared in class 'CommissionEmployee'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(35) :
 see declaration of 'CommissionEmployee::socialSecurityNumber'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(10) :
 see declaration of 'CommissionEmployee'

 C:cpphtp5_examplesch12Fig12_10_11BasePlusCommission-Employee.cpp(44) :
 error C2248: 'CommissionEmployee::grossSales' :
 cannot access private member declared in class 'CommissionEmployee'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(36) :
 see declaration of 'CommissionEmployee::grossSales'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(10) :
 see declaration of 'CommissionEmployee'

 C:cpphtp5_examplesch12Fig12_10_11BasePlusCommission-Employee.cpp(45) :
 error C2248: 'CommissionEmployee::commissionRate' :
 cannot access private member declared in class 'CommissionEmployee'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(37) :
 see declaration of 'CommissionEmployee::commissionRate'
 C:cpphtp5_examplesch12Fig12_10_11CommissionEmployee.h(10) :
 see declaration of 'CommissionEmployee'
 

Figure 12.11 shows BasePlusCommissionEmployee's member-function implementations. The constructor (lines 1017) introduces base-class initializer syntax (line 14), which uses a member initializer to pass arguments to the base-class (CommissionEmployee) constructor. C++ requires a derived-class constructor to call its base-class constructor to initialize the base-class data members that are inherited into the derived class. Line 14 accomplishes this task by invoking the CommissionEmployee constructor by name, passing the constructor's parameters first, last, ssn, sales and rate as arguments to initialize base-class data members firstName, lastName, socialSecurityNumber, grossSales and commissionRate. If BasePlusCommissionEmployee's constructor did not invoke class CommissionEmployee's constructor explicitly, C++ would attempt to invoke class CommissionEmployee's default constructorbut 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 and any attempts to implicitly call CommissionEmployee's default constructor would result in compilation errors.


Common Programming Error 12.1

A compilation error occurs if a derived-class constructor calls one of its base-class constructors with arguments that are inconsistent with the number and types of parameters specified in one of the base-class constructor definitions.

Performance Tip 12.1

In a derived-class constructor, initializing member objects and invoking base-class constructors 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.

The compiler generates errors for line 35 of Fig. 12.11 because base class CommissionEmployee's data members commissionRate and grossSales are privatederived class BasePlusCommissionEmployee's member functions are not allowed to access base class CommissionEmployee's private data. Note that we used red text in Fig. 12.11 to indicate erroneous code. The compiler issues additional errors at lines 4245 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. [Note: To save space, we show only the error messages from Visual C++ .NET in this example. The error messages produced by your compiler may differ from those shown here. Also notice that we highlight key portions of the lengthy error messages in bold.]


We purposely included the erroneous code in Fig. 12.11 to demonstrate 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 35 could have invoked getCommissionRate and getGrossSales to access CommissionEmployee's private data members commissionRate and grossSales, respectively. Similarly, lines 4245 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 File in the Derived Class Header File with #include

Notice that we #include the base class's header file in the derived class's header file (line 10 of Fig. 12.10). This is necessary for three reasons. First, for the derived class to use the base class's name in line 12, we must tell the compiler that the base class existsthe 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.8). A client program that creates an object of a class must #include 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 10 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 10 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. 12.1012.11, the compiler uses the base-class header file 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 functionsyou will see an example of such a function call in Fig. 12.16.

Linking Process in an Inheritance Hierarchy

In Section 3.9, 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 will see an example of this in Section 12.4.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.


12.4.4. CommissionEmployee-BasePlusCommissionEmployee Inheritance Hierarchy Using protected Data

To enable class BasePlusCommissionEmployee to directly access CommissionEmployee data members firstName, lastName, socialSecurityNumber, grossSales and commissionRate, we can declare those members as protected in the base class. As we discussed in Section 12.3, a base class's protected members can be accessed by members and friends of the base class and by members and friends of any classes derived from that base class.

Good Programming Practice 12.1

Declare public members first, protected members second and private members last.

 

Defining Base Class CommissionEmployee with protected Data

Class CommissionEmployee (Figs. 12.1212.13) now declares data members firstName, lastName, socialSecurityNumber, grossSales and commissionRate as protected (Fig. 12.12, lines 3337) rather than private. The member-function implementations in Fig. 12.13 are identical to those in Fig. 12.5.

Figure 12.12. CommissionEmployee class definition that declares protected data to allow access by derived classes.

(This item is displayed on pages 655 - 656 in the print version)

 1 // Fig. 12.12: CommissionEmployee.h
 2 // CommissionEmployee class definition with protected data.
 3 #ifndef COMMISSION_H
 4 #define COMMISSION_H
 5
 6 #include  // C++ standard string class
 7 using std::string;
 8
 9 class CommissionEmployee
10 {
11 public:
12 CommissionEmployee( const string &, const string &, const string &,
13 double = 0.0, double = 0.0 );
14
15 void setFirstName( const string & ); // set first name
16 string getFirstName() const; // return first name
17
18 void setLastName( const string & ); // set last name
19 string getLastName() const; // return last name
20
21 void setSocialSecurityNumber( const string & ); // set SSN
22 string getSocialSecurityNumber() const; // return SSN
23
24 void setGrossSales( double ); // set gross sales amount
25 double getGrossSales() const; // return gross sales amount
26
27 void setCommissionRate( double ); // set commission rate
28 double getCommissionRate() const; // return commission rate
29
30 double earnings() const; // calculate earnings
31 void print() const; // print CommissionEmployee object
32 protected:
33 string firstName; 
34 string lastName; 
35 string socialSecurityNumber; 
36 double grossSales; // gross weekly sales 
37 double commissionRate; // commission percentage
38 }; // end class CommissionEmployee
39
40 #endif

Figure 12.13. CommissionEmployee class with protected data.

(This item is displayed on pages 656 - 658 in the print version)

 1 // Fig. 12.13: CommissionEmployee.cpp
 2 // Class CommissionEmployee member-function definitions.
 3 #include 
 4 using std::cout;
 5 
 6 #include "CommissionEmployee.h"  // CommissionEmployee class definition
 7 
 8 // constructor
 9 CommissionEmployee::CommissionEmployee(
10 const string &first, const string &last, const string &ssn,
11 double sales, double rate )
12 {
13 firstName = first; // should validate
14 lastName = last; // should validate
15 socialSecurityNumber = ssn; // should validate
16 setGrossSales( sales ); // validate and store gross sales
17 setCommissionRate( rate ); // validate and store commission rate
18 } // end CommissionEmployee constructor
19 
20 // set first name
21 void CommissionEmployee::setFirstName( const string &first )
22 {
23 firstName = first; // should validate
24 } // end function setFirstName
25 
26 // return first name
27 string CommissionEmployee::getFirstName() const
28 {
29 return firstName;
30 } // end function getFirstName
31 
32 // set last name
33 void CommissionEmployee::setLastName( const string &last )
34 {
35 lastName = last; // should validate
36 } // end function setLastName
37 
38 // return last name
39 string CommissionEmployee::getLastName() const
40 {
41 return lastName;
42 } // end function getLastName
43 
44 // set social security number
45 void CommissionEmployee::setSocialSecurityNumber( const string &ssn )
46 {
47 socialSecurityNumber = ssn; // should validate
48 } // end function setSocialSecurityNumber
49 
50 // return social security number
51 string CommissionEmployee::getSocialSecurityNumber() const
52 {
53 return socialSecurityNumber;
54 } // end function getSocialSecurityNumber
55 
56 // set gross sales amount
57 void CommissionEmployee::setGrossSales( double sales )
58 {
59 grossSales = ( sales < 0.0 ) ? 0.0 : sales;
60 } // end function setGrossSales
61 
62 // return gross sales amount
63 double CommissionEmployee::getGrossSales() const
64 {
65 return grossSales;
66 } // end function getGrossSales
67 
68 // set commission rate
69 void CommissionEmployee::setCommissionRate( double rate )
70 {
71 commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0;
72 } // end function setCommissionRate
73 
74 // return commission rate
75 double CommissionEmployee::getCommissionRate() const
76 {
77 return commissionRate;
78 } // end function getCommissionRate
79 
80 // calculate earnings
81 double CommissionEmployee::earnings() const
82 {
83 return commissionRate * grossSales;
84 } // end function earnings
85 
86 // print CommissionEmployee object
87 void CommissionEmployee::print() const
88 {
89 cout << "commission employee: " << firstName << ' ' << lastName
90 << "
social security number: " << socialSecurityNumber
91 << "
gross sales: " << grossSales
92 << "
commission rate: " << commissionRate;
93 } // end function print

Modifying Derived Class BasePlusCommissionEmployee

We now modify class BasePlusCommissionEmployee (Figs. 12.1412.15) so that it inherits from the version of class CommissionEmployee in Figs. 12.1212.13. Because class BasePlusCommissionEmployee inherits from this version of class CommissionEmployee, objects of class BasePlusCommissionEmployee can access inherited data members that are declared protected in class CommissionEmployee (i.e., data members firstName, lastName, socialSecurityNumber, grossSales and commissionRate). As a result, the compiler does not generate errors when compiling the BasePlusCommissionEmployee earnings and print member-function definitions in Fig. 12.15 (lines 3236 and 3947, respectively). This shows the special privileges that a derived class is granted to access protected baseclass data members. Objects of a derived class also can access protected members in any of that derived class's indirect base classes.


Figure 12.14. BasePlusCommissionEmployee class header file.

(This item is displayed on page 658 in the print version)

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

Figure 12.15. BasePlusCommissionEmployee implementation file for BasePlusCommissionEmployee class that inherits protected data from CommissionEmployee.

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

Class BasePlusCommissionEmployee does not inherit class CommissionEmployee's constructor. However, class BasePlusCommissionEmployee's constructor (Fig. 12.15, lines 1017) calls class CommissionEmployee's constructor explicitly (line 14). Recall that BasePlusCommissionEmployee's constructor must explicitly call the constructor of class CommissionEmployee, because CommissionEmployee does not contain a default constructor that could be invoked implicitly.

Testing the Modified BasePlusCommissionEmployee Class

Figure 12.16 uses a BasePlusCommissionEmployee object to perform the same tasks that Fig. 12.9 performed on an object of the first version of class BasePlusCommissionEmployee (Figs. 12.712.8). Note that the outputs of the two programs are identical. We created the first class BasePlusCommissionEmployee without using inheritance and created this version of BasePlusCommissionEmployee using inheritance; however, both classes provide the same functionality. Note that the code for class BasePlusCommissionEmployee (i.e., the header and implementation files), which is 74 lines, is considerably shorter than the code for the noninherited version of the class, which is 154 lines, because the inherited version absorbs part of its functionality from CommissionEmployee, whereas the noninherited version does not absorb any functionality. Also, there is now only one copy of the CommissionEmployee functionality declared and defined in class CommissionEmployee. This makes the source code easier to maintain, modify and debug, because the source code related to a CommissionEmployee exists only in the files of Figs. 12.1212.13.


Figure 12.16. protected base-class data can be accessed from derived class.

(This item is displayed on pages 660 - 661 in the print version)

 1 // Fig. 12.16: fig12_16.cpp
 2 // Testing class BasePlusCommissionEmployee.
 3 #include 
 4 using std::cout;
 5 using std::endl;
 6 using std::fixed;
 7
 8 #include 
 9 using std::setprecision;
10
11 // BasePlusCommissionEmployee class definition
12 #include "BasePlusCommissionEmployee.h"
13
14 int main()
15 {
16 // instantiate BasePlusCommissionEmployee object
17 BasePlusCommissionEmployee
18 employee( "Bob", "Lewis", "333-33-3333", 5000, .04, 300 );
19
20 // set floating-point output formatting
21 cout << fixed << setprecision( 2 );
22
23 // get commission employee data
24 cout << "Employee information obtained by get functions: 
"
25 << "
First name is " << employee.getFirstName()
26 << "
Last name is " << employee.getLastName()
27 << "
Social security number is "
28 << employee.getSocialSecurityNumber()
29 << "
Gross sales is " << employee.getGrossSales()
30 << "
Commission rate is " << employee.getCommissionRate()
31 << "
Base salary is " << employee.getBaseSalary() << endl;
32
33 employee.setBaseSalary( 1000 ); // set base salary
34
35 cout << "
Updated employee information output by print function: 
"
36 << endl;
37 employee.print(); // display the new employee information
38
39 // display the employee's earnings
40 cout << "

Employee's earnings: $" << employee.earnings() << endl;
41
42 return 0;
43 } // end main
 
 Employee information obtained by get functions:

 First name is Bob
 Last name is Lewis
 Social security number is 333-33-3333
 Gross sales is 5000.00
 Commission rate is 0.04
 Base salary is 300.00

 Updated employee information output by print function:

 base-salaried commission employee: Bob Lewis
 social security number: 333-33-3333
 gross sales: 5000.00
 commission rate: 0.04
 base salary: 1000.00

 Employee's earnings: $1200.00
 

Notes on Using protected Data

In this example, we declared base-class data members as protected, so that derived classes could modify the data directly. Inheriting protected data members slightly increases performance, because we can directly access the members without incurring the overhead of calls to set or get member functions. In most cases, however, it is better to use private data members to encourage proper software engineering, and leave code optimization issues to the compiler. Your code will be easier to maintain, modify and debug.

Using protected data members creates two major problems. First, the derived-class object does not have to use a member function to set the value of the base-class's protected data member. Therefore, a derived-class object easily can assign an invalid value to the protected data member, thus leaving the object in an inconsistent state. For example, with CommissionEmployee's data member grossSales declared as protected, a derived-class (e.g., BasePlusCommissionEmployee) object can assign a negative value to grossSales. The second problem with using protected data members is that derived-class member functions are more likely to be written so that they depend on the base-class implementation. In practice, derived classes should depend only on the base-class services (i.e., non private member functions) and not on the base-class implementation. With protected data members in the base class, if the base-class implementation changes, we may need to modify all derived classes of that base class. For example, if for some reason we were to change the names of data members firstName and lastName to first and last, then we would have to do so for all occurrences in which a derived class references these base-class data members directly. In such a case, the software is said to be fragile or brittle, because a small change in the base class can "break" derived-class implementation. The programmer should be able to change the base-class implementation while still providing the same services to derived classes. (Of course, if the base-class services change, we must reimplement our derived classesgood object-oriented design attempts to prevent this.)


Software Engineering Observation 12.5

It is appropriate to use the protected access specifier when a base class should provide a service (i.e., a member function) only to its derived classes (and friends), not to other clients.

Software Engineering Observation 12.6

Declaring base-class data members private (as opposed to declaring them protected) enables programmers to change the base-class implementation without having to change derived-class implementations.

Error-Prevention Tip 12.1

When possible, avoid including protected data members in a base class. Rather, include non-private member functions that access private data members, ensuring that the object maintains a consistent state.

 

12.4.5. CommissionEmployee-BasePlusCommissionEmployee Inheritance Hierarchy Using private Data

We now reexamine our hierarchy once more, this time using the best software engineering practices. Class CommissionEmployee (Figs. 12.1712.18) now declares data members firstName, lastName, socialSecurityNumber, grossSales and commissionRate as private (Fig. 12.17, lines 3337) and provides public member functions setFirstName, getFirstName, setLastName, getLastName, setSocialSecurityNumber, getSocialSecurityNumber, setGrossSales, getGrossSales, setCommissionRate, getCommissionRate, earnings and print for manipulating these values. If we decide to change the data member names, the earnings and print definitions will not require modificationonly the definitions of the get and set member functions that directly manipulate the data members will need to change. Note that these changes occur solely within the base classno changes to the derived class are needed. Localizing the effects of changes like this is a good software engineering practice. Derived class BasePlusCommissionEmployee (Figs. 12.1912.20) inherits CommissionEmployee's non-private member functions and can access the private base-class members via those member functions.


Figure 12.17. CommissionEmployee class defined using good software engineering practices.

 1 // Fig. 12.17: CommissionEmployee.h
 2 // CommissionEmployee class definition with good software engineering.
 3 #ifndef COMMISSION_H
 4 #define COMMISSION_H
 5
 6 #include  // C++ standard string class
 7 using std::string;
 8
 9 class CommissionEmployee
10 {
11 public:
12 CommissionEmployee( const string &, const string &, const string &,
13 double = 0.0, double = 0.0 );
14
15 void setFirstName( const string & ); // set first name
16 string getFirstName() const; // return first name
17
18 void setLastName( const string & ); // set last name
19 string getLastName() const; // return last name
20
21 void setSocialSecurityNumber( const string & ); // set SSN
22 string getSocialSecurityNumber() const; // return SSN
23
24 void setGrossSales( double ); // set gross sales amount
25 double getGrossSales() const; // return gross sales amount
26
27 void setCommissionRate( double ); // set commission rate
28 double getCommissionRate() const; // return commission rate
29
30 double earnings() const; // calculate earnings
31 void print() const; // print CommissionEmployee object
32 private:
33 string firstName; 
34 string lastName; 
35 string socialSecurityNumber; 
36 double grossSales; // gross weekly sales 
37 double commissionRate; // commission percentage
38 }; // end class CommissionEmployee
39
40 #endif

Figure 12.18. CommissionEmployee class implementation file: CommissionEmployee class uses member functions to manipulate its private data.

(This item is displayed on pages 663 - 665 in the print version)

 1 // Fig. 12.18: CommissionEmployee.cpp
 2 // Class CommissionEmployee member-function definitions.
 3 #include 
 4 using std::cout;
 5
 6 #include "CommissionEmployee.h" // CommissionEmployee class definition
 7
 8 // constructor
 9 CommissionEmployee::CommissionEmployee(
10 const string &first, const string &last, const string &ssn,
11 double sales, double rate )
12 : firstName( first ), lastName( last ), socialSecurityNumber( ssn )
13 {
14 setGrossSales( sales ); // validate and store gross sales
15 setCommissionRate( rate ); // validate and store commission rate
16 } // end CommissionEmployee constructor
17
18 // set first name
19 void CommissionEmployee::setFirstName( const string &first )
20 {
21 firstName = first; // should validate
22 } // end function setFirstName
23
24 // return first name
25 string CommissionEmployee::getFirstName() const
26 {
27 return firstName;
28 } // end function getFirstName
29
30 // set last name
31 void CommissionEmployee::setLastName( const string &last )
32 {
33 lastName = last; // should validate
34 } // end function setLastName
35
36 // return last name
37 string CommissionEmployee::getLastName() const
38 {
39 return lastName;
40 } // end function getLastName
41
42 // set social security number
43 void CommissionEmployee::setSocialSecurityNumber( const string &ssn )
44 {
45 socialSecurityNumber = ssn; // should validate
46 } // end function setSocialSecurityNumber
47
48 // return social security number
49 string CommissionEmployee::getSocialSecurityNumber() const
50 {
51 return socialSecurityNumber;
52 } // end function getSocialSecurityNumber
53
54 // set gross sales amount
55 void CommissionEmployee::setGrossSales( double sales )
56 {
57 grossSales = ( sales < 0.0 ) ? 0.0 : sales;
58 } // end function setGrossSales
59
60 // return gross sales amount
61 double CommissionEmployee::getGrossSales() const
62 {
63 return grossSales;
64 } // end function getGrossSales
65
66 // set commission rate
67 void CommissionEmployee::setCommissionRate( double rate )
68 {
69 commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0;
70 } // end function setCommissionRate
71
72 // return commission rate
73 double CommissionEmployee::getCommissionRate() const
74 {
75 return commissionRate;
76 } // end function getCommissionRate
77
78 // calculate earnings
79 double CommissionEmployee::earnings() const
80 {
81 return getCommissionRate() * getGrossSales();
82 } // end function earnings
83
84 // print CommissionEmployee object
85 void CommissionEmployee::print() const
86 {
87 cout << "commission employee: "
88 << getFirstName() << ' ' << getLastName()
89 << "
social security number: " << getSocialSecurityNumber()
90 << "
gross sales: " << getGrossSales()
91 << "
commission rate: " << getCommissionRate();
92 } // end function print

Figure 12.19. BasePlusCommissionEmployee class header file.

(This item is displayed on page 666 in the print version)

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

Figure 12.20. BasePlusCommissionEmployee class that inherits from class CommissionEmployee but cannot directly access the class's private data.

(This item is displayed on pages 666 - 667 in the print version)

 1 // Fig. 12.20: BasePlusCommissionEmployee.cpp
 2 // Class BasePlusCommissionEmployee member-function definitions.
 3 #include 
 4 using std::cout;
 5
 6 // BasePlusCommissionEmployee class definition
 7 #include "BasePlusCommissionEmployee.h"
 8
 9 // constructor
10 BasePlusCommissionEmployee::BasePlusCommissionEmployee(
11 const string &first, const string &last, const string &ssn,
12 double sales, double rate, double salary )
13 // explicitly call base-class constructor
14 : CommissionEmployee( first, last, ssn, sales, rate )
15 {
16 setBaseSalary( salary ); // validate and store base salary
17 } // end BasePlusCommissionEmployee constructor
18
19 // set base salary
20 void BasePlusCommissionEmployee::setBaseSalary( double salary )
21 {
22 baseSalary = ( salary < 0.0 ) ? 0.0 : salary;
23 } // end function setBaseSalary
24
25 // return base salary
26 double BasePlusCommissionEmployee::getBaseSalary() const
27 {
28 return baseSalary;
29 } // end function getBaseSalary
30
31 // calculate earnings
32 double BasePlusCommissionEmployee::earnings() const
33 {
34 return getBaseSalary() + CommissionEmployee::earnings();
35 } // end function earnings
36
37 // print BasePlusCommissionEmployee object
38 void BasePlusCommissionEmployee::print() const
39 {
40 cout << "base-salaried ";
41
42 // invoke CommissionEmployee's print function
43 CommissionEmployee::print(); 
44
45 cout << "
base salary: " << getBaseSalary();
46 } // end function print

In the CommissionEmployee constructor implementation (Fig. 12.18, lines 916), note that we use member initializers (line 12) to set the values of members firstName, lastName and socialSecurityNumber. We show how derived-class BasePlusCommissionEmployee (Figs. 12.1912.20) can invoke non-private base-class member functions (setFirstName, getFirstName, setLastName, getLastName, setSocialSecurityNumber and getSocialSecurityNumber) to manipulate these data members.

Performance Tip 12.2

Using a member function to access a data member's value can be slightly slower than accessing the data directly. However, today's optimizing compilers are carefully designed to perform many optimizations implicitly (such as inlining set and get member-function calls). As a result, programmers should write code that adheres to proper software engineering principles, and leave optimization issues to the compiler. A good rule is, "Do not second-guess the compiler."


Class BasePlusCommissionEmployee (Figs. 12.1912.20) has several changes to its member-function implementations (Fig. 12.20) that distinguish it from the previous version of the class (Figs. 12.1412.15). Member functions earnings (Fig. 12.20, lines 3235) and print (lines 3846) each invoke member function getBaseSalary to obtain the base salary value, rather than accessing baseSalary directly. This insulates earnings and print from potential changes to the implementation of data member baseSalary. For example, if we decide to rename data member baseSalary or change its type, only member functions setBaseSalary and getBaseSalary will need to change.


Class BasePlusCommissionEmployee's earnings function (Fig. 12.20, lines 3235) redefines class CommissionEmployee's earnings member function (Fig. 12.18, lines 7982) to calculate the earnings of a base-salaried commission employee. Class BasePlusCommissionEmployee's version of earnings obtains the portion of the employee's earnings based on commission alone by calling base-class CommissionEmployee's earnings function with the expression CommissionEmployee::earnings() (Fig. 12.20, line 34). BasePlusCommissionEmployee's earnings function then adds the base salary to this value to calculate the total earnings of the employee. Note the syntax used to invoke a redefined base-class member function from a derived classplace the base-class name and the binary scope resolution operator (::) before the base-class member-function name. This member-function invocation is a good software engineering practice: Recall from Software Engineering Observation 9.9 that, if an object's member function performs the actions needed by another object, we should call that member function rather than duplicating its code body. By having BasePlusCommissionEmployee's earnings function invoke CommissionEmployee's earnings function to calculate part of a BasePlusCommissionEmployee object's earnings, we avoid duplicating the code and reduce code-maintenance problems.


Common Programming Error 12.2

When a base-class member function is redefined in a derived class, the derived-class version often calls the base-class version to do additional work. Failure to use the :: operator prefixed with the name of the base class when referencing the base class's member function causes infinite recursion, because the derived-class member function would then call itself.

Common Programming Error 12.3

Including a base-class member function with a different signature in the derived class hides the base-class version of the function. Attempts to call the base-class version through the public interface of a derived-class object result in compilation errors.

Similarly, BasePlusCommissionEmployee's print function (Fig. 12.20, lines 3846) redefines class CommissionEmployee's print member function (Fig. 12.18, lines 8592) to output information that is appropriate for a base-salaried commission employee. Class BasePlusCommissionEmployee's version displays part of a BasePlusCommissionEmployee object's information (i.e., the string "commission employee" and the values of class CommissionEmployee's private data members) by calling CommissionEmployee's print member function with the qualified name CommissionEmployee::print() (Fig. 12.20, line 43). BasePlusCommissionEmployee's print function then outputs the remainder of a BasePlusCommissionEmployee object's information (i.e., the value of class BasePlusCommissionEmployee's base salary).

Figure 12.21 performs the same manipulations on a BasePlusCommissionEmployee object as did Fig. 12.9 and Fig. 12.16 on objects of classes CommissionEmployee and BasePlusCommissionEmployee, respectively. Although each "base-salaried commission employee" class behaves identically, class BasePlusCommissionEmployee is the best engineered. By using inheritance and by calling member functions that hide the data and ensure consistency, we have efficiently and effectively constructed a well-engineered class.

Figure 12.21. Base-class private data is accessible to a derived class via public or protected member function inherited by the derived class.

(This item is displayed on pages 668 - 669 in the print version)

 1 // Fig. 12.21: fig12_21.cpp
 2 // Testing class BasePlusCommissionEmployee.
 3 #include 
 4 using std::cout;
 5 using std::endl;
 6 using std::fixed;
 7
 8 #include 
 9 using std::setprecision;
10
11 // BasePlusCommissionEmployee class definition
12 #include "BasePlusCommissionEmployee.h"
13
14 int main()
15 {
16 // instantiate BasePlusCommissionEmployee object
17 BasePlusCommissionEmployee
18 employee( "Bob", "Lewis", "333-33-3333", 5000, .04, 300 );
19
20 // set floating-point output formatting
21 cout << fixed << setprecision( 2 );
22
23 // get commission employee data
24 cout << "Employee information obtained by get functions: 
"
25 << "
First name is " << employee.getFirstName()
26 << "
Last name is " << employee.getLastName()
27 << "
Social security number is "
28 << employee.getSocialSecurityNumber()
29 << "
Gross sales is " << employee.getGrossSales()
30 << "
Commission rate is " << employee.getCommissionRate()
31 << "
Base salary is " << employee.getBaseSalary() << endl;
32
33 employee.setBaseSalary( 1000 ); // set base salary
34
35 cout << "
Updated employee information output by print function: 
"
36 << endl;
37 employee.print(); // display the new employee information
38
39 // display the employee's earnings
40 cout << "

Employee's earnings: $" << employee.earnings() << endl;
41
42 return 0;
43 } // end main
 
 Employee information obtained by get functions:

 First name is Bob
 Last name is Lewis
 Social security number is 333-33-3333
 Gross sales is 5000.00
 Commission rate is 0.04
 Base salary is 300.00

 Updated employee information output by print function:

 base-salaried commission employee: Bob Lewis
 social security number: 333-33-3333
 gross sales: 5000.00
 commission rate: 0.04
 base salary: 1000.00

 Employee's earnings: $1200.00
 

In this section, you saw an evolutionary set of examples that was carefully designed to teach key capabilities for good software engineering with inheritance. You learned how to create a derived class using inheritance, how to use protected base-class members to enable a derived class to access inherited base-class data members and how to redefine baseclass functions to provide versions that are more appropriate for derived-class objects. In addition, you learned how to apply software engineering techniques from Chapters 910 and this chapter to create classes that are easy to maintain, modify and debug.





C++ How to Program
C++ How to Program (5th Edition)
ISBN: 0131857576
EAN: 2147483647
Year: 2004
Pages: 627
Simiral book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net