Constructors and Destructors in Derived Classes
As we explained in the preceding section, instantiating a derived-class object begins a chain of constructor calls in which the derived-class constructor, before performing its own tasks, invokes its direct base class's constructor either explicitly (via a base-class member initializer) or implicitly (calling the base class's default constructor). Similarly, if the base class is derived from another class, the base-class constructor is required to invoke the constructor of the next class up in the hierarchy, and so on. The last constructor called in this chain is the constructor of the class at the base of the hierarchy, whose body actually finishes executing first. The original derived-class constructor's body finishes executing last. Each base-class constructor initializes the base-class data members that the derived-class object inherits. For example, consider the CommissionEmployee/BasePlusCommissionEmployee hierarchy from Figs. 12.1712.20. When a program creates an object of class BasePlusCommissionEmployee, the CommissionEmployee constructor is called. Since class CommissionEmployee is at the base of the hierarchy, its constructor executes, initializing the private data members of CommissionEmployee that are part of the BasePlusCommissionEmployee object. When CommissionEmployee's constructor completes execution, it returns control to BasePlusCommissionEmployee's constructor, which initializes the BasePlusCommissionEmployee object's baseSalary.
Software Engineering Observation 12.7
When a program creates a derived-class object, the derived-class constructor immediately calls the base-class constructor, the base-class constructor's body executes, then the derived class's member initializers execute and finally the derived-class constructor's body executes. This process cascades up the hierarchy if the hierarchy contains more than two levels. |
When a derived-class object is destroyed, the program calls that object's destructor. This begins a chain (or cascade) of destructor calls in which the derived-class destructor and the destructors of the direct and indirect base classes and the classes' members execute in reverse of the order in which the constructors executed. When a derived-class object's destructor is called, the destructor performs its task, then invokes the destructor of the next base class up the hierarchy. This process repeats until the destructor of the final base class at the top of the hierarchy is called. Then the object is removed from memory.
Software Engineering Observation 12.8
Suppose that we create an object of a derived class where both the base class and the derived class contain objects of other classes. When an object of that derived class is created, first the constructors for the base class's member objects execute, then the base-class constructor executes, then the constructors for the derived class's member objects execute, then the derived class's constructor executes. Destructors for derived-class objects are called in the reverse of the order in which their corresponding constructors are called. |
Base-class constructors, destructors and overloaded assignment operators (see Chapter 11, Operator Overloading; String and Array Objects) are not inherited by derived classes. Derived-class constructors, destructors and overloaded assignment operators, however, can call base-class constructors, destructors and overloaded assignment operators.
Our next example revisits the commission employee hierarchy by defining class CommissionEmployee (Figs. 12.2212.23) and class BasePlusCommissionEmployee (Figs. 12.2412.25) that contain constructors and destructors, each of which prints a message when it is invoked. As you will see in the output in Fig. 12.26, these messages demonstrate the order in which the constructors and destructors are called for objects in an inheritance hierarchy.
Figure 12.22. CommissionEmployee class header file.
1 // Fig. 12.22: 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 ~CommissionEmployee(); // destructor 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 double earnings() const; // calculate earnings 32 void print() const; // print CommissionEmployee object 33 private: 34 string firstName; 35 string lastName; 36 string socialSecurityNumber; 37 double grossSales; // gross weekly sales 38 double commissionRate; // commission percentage 39 }; // end class CommissionEmployee 40 41 #endif |
Figure 12.23. CommissionEmployee's constructor outputs text.
(This item is displayed on pages 672 - 674 in the print version)
1 // Fig. 12.23: CommissionEmployee.cpp 2 // Class CommissionEmployee member-function definitions. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include "CommissionEmployee.h" // CommissionEmployee class definition 8 9 // constructor 10 CommissionEmployee::CommissionEmployee( 11 const string &first, const string &last, const string &ssn, 12 double sales, double rate ) 13 : firstName( first ), lastName( last ), socialSecurityNumber( ssn ) 14 { 15 setGrossSales( sales ); // validate and store gross sales 16 setCommissionRate( rate ); // validate and store commission rate 17 18 cout << "CommissionEmployee constructor: " << endl; 19 print(); 20 cout << " "; 21 } // end CommissionEmployee constructor 22 23 // destructor 24 CommissionEmployee::~CommissionEmployee() 25 { 26 cout << "CommissionEmployee destructor: " << endl; 27 print(); 28 cout << " "; 29 } // end CommissionEmployee destructor 30 31 // set first name 32 void CommissionEmployee::setFirstName( const string &first ) 33 { 34 firstName = first; // should validate 35 } // end function setFirstName 36 37 // return first name 38 string CommissionEmployee::getFirstName() const 39 { 40 return firstName; 41 } // end function getFirstName 42 43 // set last name 44 void CommissionEmployee::setLastName( const string &last ) 45 { 46 lastName = last; // should validate 47 } // end function setLastName 48 49 // return last name 50 string CommissionEmployee::getLastName() const 51 { 52 return lastName; 53 } // end function getLastName 54 55 // set social security number 56 void CommissionEmployee::setSocialSecurityNumber( const string &ssn ) 57 { 58 socialSecurityNumber = ssn; // should validate 59 } // end function setSocialSecurityNumber 60 61 // return social security number 62 string CommissionEmployee::getSocialSecurityNumber() const 63 { 64 return socialSecurityNumber; 65 } // end function getSocialSecurityNumber 66 67 // set gross sales amount 68 void CommissionEmployee::setGrossSales( double sales ) 69 { 70 grossSales = ( sales < 0.0 ) ? 0.0 : sales; 71 } // end function setGrossSales 72 73 // return gross sales amount 74 double CommissionEmployee::getGrossSales() const 75 { 76 return grossSales; 77 } // end function getGrossSales 78 79 // set commission rate 80 void CommissionEmployee::setCommissionRate( double rate ) 81 { 82 commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0; 83 } // end function setCommissionRate 84 85 // return commission rate 86 double CommissionEmployee::getCommissionRate() const 87 { 88 return commissionRate; 89 } // end function getCommissionRate 90 91 // calculate earnings 92 double CommissionEmployee::earnings() const 93 { 94 return getCommissionRate() * getGrossSales(); 95 } // end function earnings 96 97 // print CommissionEmployee object 98 void CommissionEmployee::print() const 99 { 100 cout << "commission employee: " 101 << getFirstName() << ' ' << getLastName() 102 << " social security number: " << getSocialSecurityNumber() 103 << " gross sales: " << getGrossSales() 104 << " commission rate: " << getCommissionRate(); 105 } // end function print |
Figure 12.24. BasePlusCommissionEmployee class header file.
(This item is displayed on page 674 in the print version)
1 // Fig. 12.24: 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 ~BasePlusCommissionEmployee(); // destructor 18 19 void setBaseSalary( double ); // set base salary 20 double getBaseSalary() const; // return base salary 21 22 double earnings() const; // calculate earnings 23 void print() const; // print BasePlusCommissionEmployee object 24 private: 25 double baseSalary; // base salary 26 }; // end class BasePlusCommissionEmployee 27 28 #endif |
In this example, we modified the CommissionEmployee constructor (lines 1021 of Fig. 12.23) and included a CommissionEmployee destructor (lines 2429), each of which outputs a line of text upon its invocation. We also modified the BasePlusCommissionEmployee constructor (lines 1122 of Fig. 12.25) and included a BasePlusCommissionEmployee destructor (lines 2530), each of which outputs a line of text upon its invocation.
Figure 12.25. BasePlusCommissionEmployee's constructor outputs text.
(This item is displayed on pages 674 - 675 in the print version)
1 // Fig. 12.25: BasePlusCommissionEmployee.cpp 2 // Class BasePlusCommissionEmployee member-function definitions. 3 #include 4 using std::cout; 5 using std::endl; 6 7 // BasePlusCommissionEmployee class definition 8 #include "BasePlusCommissionEmployee.h" 9 10 // constructor 11 BasePlusCommissionEmployee::BasePlusCommissionEmployee( 12 const string &first, const string &last, const string &ssn, 13 double sales, double rate, double salary ) 14 // explicitly call base-class constructor 15 : CommissionEmployee( first, last, ssn, sales, rate ) 16 { 17 setBaseSalary( salary ); // validate and store base salary 18 19 cout << "BasePlusCommissionEmployee constructor: " << endl; 20 print(); 21 cout << " "; 22 } // end BasePlusCommissionEmployee constructor 23 24 // destructor 25 BasePlusCommissionEmployee::~BasePlusCommissionEmployee() 26 { 27 cout << "BasePlusCommissionEmployee destructor: " << endl; 28 print(); 29 cout << " "; 30 } // end BasePlusCommissionEmployee destructor 31 32 // set base salary 33 void BasePlusCommissionEmployee::setBaseSalary( double salary ) 34 { 35 baseSalary = ( salary < 0.0 ) ? 0.0 : salary; 36 } // end function setBaseSalary 37 38 // return base salary 39 double BasePlusCommissionEmployee::getBaseSalary() const 40 { 41 return baseSalary; 42 } // end function getBaseSalary 43 44 // calculate earnings 45 double BasePlusCommissionEmployee::earnings() const 46 { 47 return getBaseSalary() + CommissionEmployee::earnings(); 48 } // end function earnings 49 50 // print BasePlusCommissionEmployee object 51 void BasePlusCommissionEmployee::print() const 52 { 53 cout << "base-salaried "; 54 55 // invoke CommissionEmployee's print function 56 CommissionEmployee::print(); 57 58 cout << " base salary: " << getBaseSalary(); 59 } // end function print |
Figure 12.26 demonstrates the order in which constructors and destructors are called for objects of classes that are part of an inheritance hierarchy. Function main (lines 1534) begins by instantiating CommissionEmployee object employee1 (lines 2122) in a separate block inside main (lines 2023). The object goes in and out of scope immediately (the end of the block is reached as soon as the object is created), so both the CommissionEmployee constructor and destructor are called. Next, lines 2627 instantiate BasePlusCommissionEmployee object employee2. This invokes the CommissionEmployee constructor to display outputs with values passed from the BasePlusCommissionEmployee constructor, then the output specified in the BasePlusCommissionEmployee constructor is performed. Lines 3031 then instantiate BasePlusCommissionEmployee object employee3. Again, the CommissionEmployee and BasePlusCommissionEmployee constructors are both called. Note that, in each case, the body of the CommissionEmployee constructor is executed before the body of the BasePlusCommissionEmployee constructor executes. When the end of main is reached, the destructors are called for objects employee2 and employee3. But, because destructors are called in the reverse order of their corresponding constructors, the BasePlusCommissionEmployee destructor and CommissionEmployee destructor are called (in that order) for object employee3, then the BasePlusCommissionEmployee and CommissionEmployee destructors are called (in that order) for object employee2.
Figure 12.26. Constructor and destructor call order.
(This item is displayed on pages 676 - 678 in the print version)
1 // Fig. 12.26: fig12_26.cpp 2 // Display order in which base-class and derived-class constructors 3 // and destructors are called. 4 #include 5 using std::cout; 6 using std::endl; 7 using std::fixed; 8 9 #include 10 using std::setprecision; 11 12 // BasePlusCommissionEmployee class definition 13 #include "BasePlusCommissionEmployee.h" 14 15 int main() 16 { 17 // set floating-point output formatting 18 cout << fixed << setprecision( 2 ); 19 20 { // begin new scope 21 CommissionEmployee employee1( 22 "Bob", "Lewis", "333-33-3333", 5000, .04 ); 23 } // end scope 24 25 cout << endl; 26 BasePlusCommissionEmployee 27 employee2( "Lisa", "Jones", "555-55-5555", 2000, .06, 800 ); 28 29 cout << endl; 30 BasePlusCommissionEmployee 31 employee3( "Mark", "Sands", "888-88-8888", 8000, .15, 2000 ); 32 cout << endl; 33 return 0; 34 } // end main
|