An AlarmClock object needs to know when it is supposed to sound its alarm, so why not include a Time object as a member of the AlarmClock class? Such a capability is called composition and is sometimes referred to as a has-a relationship. A class can have objects of other classes as members.
Software Engineering Observation 10.5
A common form of software reusability is composition, in which a class has objects of other classes as members. |
When an object is created, its constructor is called automatically. Previously, we saw how to pass arguments to the constructor of an object we created in main. This section shows how an object's constructor can pass arguments to member-object constructors, which is accomplished via member initializers. Member objects are constructed in the order in which they are declared in the class definition (not in the order they are listed in the constructor's member initializer list) and before their enclosing class objects (sometimes called host objects) are constructed.
The program of Figs. 10.1010.14 uses class Date (Figs. 10.1010.11) and class Employee (Figs. 10.1210.13) to demonstrate objects as members of other objects. The definition of class Employee (Fig. 10.12) contains private data members firstName, lastName, birthDate and hireDate. Members birthDate and hireDate are const objects of class Date, which contains private data members month, day and year. The Employee constructor's header (Fig. 10.13, lines 1821) specifies that the constructor receives four parameters (first, last, dateOfBirth and dateOfHire). The first two parameters are used in the constructor's body to initialize the character arrays firstName and lastName. The last two parameters are passed via member initializers to the constructor for class Date. The colon (:) in the header separates the member initializers from the parameter list. The member initializers specify the Employee constructor parameters being passed to the constructors of the member Date objects. Parameter dateOfBirth is passed to object birthDate's constructor (Fig. 10.13, line 20), and parameter dateOfHire is passed to object hireDate's constructor (Fig. 10.13, line 21). Again, member initializers are separated by commas. As you study class Date (Fig. 10.10), notice that the class does not provide a constructor that receives a parameter of type Date. So, how is the member initializer list in class Employee's constructor able to initialize the birthDate and hireDate objects by passing Date object's to their Date constructors? As we mentioned in Chapter 9, the compiler provides each class with a default copy constructor that copies each member of the constructor's argument object into the corresponding member of the object being initialized. Chapter 11 discusses how programmers can define customized copy constructors.
Figure 10.10. Date class definition.
1 // Fig. 10.10: Date.h 2 // Date class definition; Member functions defined in Date.cpp 3 #ifndef DATE_H 4 #define DATE_H 5 6 class Date 7 { 8 public: 9 Date( int = 1, int = 1, int = 1900 ); // default constructor 10 void print() const; // print date in month/day/year format 11 ~Date(); // provided to confirm destruction order 12 private: 13 int month; // 1-12 (January-December) 14 int day; // 1-31 based on month 15 int year; // any year 16 17 // utility function to check if day is proper for month and year 18 int checkDay( int ) const; 19 }; // end class Date 20 21 #endif |
Figure 10.11. Date class member-function definitions.
(This item is displayed on pages 536 - 537 in the print version)
1 // Fig. 10.11: Date.cpp 2 // Member-function definitions for class Date. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include "Date.h" // include Date class definition 8 9 // constructor confirms proper value for month; calls 10 // utility function checkDay to confirm proper value for day 11 Date::Date( int mn, int dy, int yr ) 12 { 13 if ( mn > 0 && mn <= 12 ) // validate the month 14 month = mn; 15 else 16 { 17 month = 1; // invalid month set to 1 18 cout << "Invalid month (" << mn << ") set to 1. "; 19 } // end else 20 21 year = yr; // could validate yr 22 day = checkDay( dy ); // validate the day 23 24 // output Date object to show when its constructor is called 25 cout << "Date object constructor for date "; 26 print(); 27 cout << endl; 28 } // end Date constructor 29 30 // print Date object in form month/day/year 31 void Date::print() const 32 { 33 cout << month << '/' << day << '/' << year; 34 } // end function print 35 36 // output Date object to show when its destructor is called 37 Date::~Date() 38 { 39 cout << "Date object destructor for date "; 40 print(); 41 cout << endl; 42 } // end ~Date destructor 43 44 // utility function to confirm proper day value based on 45 // month and year; handles leap years, too 46 int Date::checkDay( int testDay ) const 47 { 48 static const int daysPerMonth[ 13 ] = 49 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 50 51 // determine whether testDay is valid for specified month 52 if ( testDay > 0 && testDay <= daysPerMonth[ month ] ) 53 return testDay; 54 55 // February 29 check for leap year 56 if ( month == 2 && testDay == 29 && ( year % 400 == 0 || 57 ( year % 4 == 0 && year % 100 != 0 ) ) ) 58 return testDay; 59 60 cout << "Invalid day (" << testDay << ") set to 1. "; 61 return 1; // leave object in consistent state if bad value 62 } // end function checkDay |
Figure 10.12. Employee class definition showing composition.
(This item is displayed on page 537 in the print version)
1 // Fig. 10.12: Employee.h 2 // Employee class definition. 3 // Member functions defined in Employee.cpp. 4 #ifndef EMPLOYEE_H 5 #define EMPLOYEE_H 6 7 #include "Date.h" // include Date class definition 8 9 class Employee 10 { 11 public: 12 Employee( const char * const, const char * const, 13 const Date &, const Date & ); 14 void print() const; 15 ~Employee(); // provided to confirm destruction order 16 private: 17 char firstName[ 25 ]; 18 char lastName[ 25 ]; 19 const Date birthDate; // composition: member object 20 const Date hireDate; // composition: member object 21 }; // end class Employee 22 23 #endif |
Figure 10.13. Employee class member-function definitions, including constructor with a member initializer list.
(This item is displayed on pages 538 - 539 in the print version)
1 // Fig. 10.13: Employee.cpp 2 // Member-function definitions for class Employee. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include // strlen and strncpy prototypes 8 using std::strlen; 9 using std::strncpy; 10 11 #include "Employee.h" // Employee class definition 12 #include "Date.h" // Date class definition 13 14 // constructor uses member initializer list to pass initializer 15 // values to constructors of member objects birthDate and hireDate 16 // [Note: This invokes the so-called "default copy constructor" which the 17 // C++ compiler provides implicitly.] 18 Employee::Employee( const char * const first, const char * const last, 19 const Date &dateOfBirth, const Date &dateOfHire ) 20 : birthDate( dateOfBirth ), // initialize birthDate 21 hireDate( dateOfHire ) // initialize hireDate 22 { 23 // copy first into firstName and be sure that it fits 24 int length = strlen( first ); 25 length = ( length < 25 ? length : 24 ); 26 strncpy( firstName, first, length ); 27 firstName[ length ] = ' |