I l@ve RuBoard Previous Section Next Section

5.8 Initialization, Destruction, and Copy

Now that num_sequence declares actual data members, we must provide for their initialization. We could leave it to each derived class to initialize these data members, but that's potentially error-prone. A better design is to provide a base class constructor to handle the initialization of all base class members.

Recall that num_sequence is an abstract base class. We cannot define an independent object of its class; rather, the num_sequence serves as a subobject of each derived class object. For this reason, we declare the base class constructor to be a protected rather than public member.

The initialization of a derived class object consists of the invocation of the base class constructor followed by that of the derived class constructor. It helps to think of the derived class object as consisting of multiple subobjects: a base class subobject initialized by the base class constructor and a derived class subobject initialized by the derived class constructor. In a three-level class hierarchy, such as the AudioBook class of Section 5.1, the derived class consists of three subobjects, each one initialized by its respective constructor.

The design requirements of a derived class constructor are twofold: Not only must it initialize the derived class data members, but it must also supply the expected values to its base class constructor. In our example, the num_sequence base class requires three values that are passed to it using the member initialization list. For example,



inline Fibonacci:: 


Fibonacci( int len, int beg_pos ) 


         : num_sequence( len, beg_pos, &_elems ) 





{} 

If we should overlook the invocation of the num_sequence constructor, the definition of the Fibonacci constructor is flagged as an error. Why? The num_sequence base class requires our explicit invocation of its three-argument constructor. In our design, this is what we want.

Alternatively, we could provide a default num_sequence constructor. We must change _relems to a pointer, however, and add code to verify that it is non-null before each access of the vector:



num_sequence:: 


num_sequence( int len=1, int bp=1, vector<int> *pe=0 ) 


      : _length( len ), _beg_pos( bp ), _pelems( re ){} 

Now, if the derived class constructor does not explicitly invoke the base class constructor, the default base class constructor is invoked automatically.

What happens when we initialize one Fibonacci class object with another?



Fibonacci fib1( 12 ); 


Fibonacci fib2 = fib1; 

If an explicit copy constructor is defined, that instance is invoked. For example, we might define a Fibonacci copy constructor as follows:



Fibonacci::Fibonacci( const Fibonacci &rhs ) 


          : num_sequence( rhs ) 


{} 

rhs, the right-hand derived class object, is passed to the base class copy constructor using the member initialization list. What if the base class does not define an explicit copy constructor? Nothing bad happens. Default memberwise initialization is carried out. If an explicit base class copy constructor is defined, it is invoked.

In this case, an explicit Fibonacci copy constructor is unnecessary because the default behavior accomplishes the same results: First the base class subobject is memberwise initialized, followed by the memberwise initialization of the derived class members.

The same is true of the copy assignment operator. If we assign one Fibonacci class object to another and if an explicit copy assignment operator is defined, it is invoked to carry out the assignment. For example, here is how we might define the operator. The only tricky part is to invoke the base class operator explicitly.



Fibonacci& Fibonacci:: 


operator=( const Fibonacci &rhs ) 


{ 


   if ( this != &rhs ) 


        // explicit invocation of the base class operator 


        num_sequence::operator=( rhs ); 





   return *this; 


} 

Again, in this case, an explicit Fibonacci copy assignment operator is unnecessary because the default behavior accomplishes the same results. (Refer to Section 4.2 and Section 4.8 for a discussion of when a copy constructor and a copy assignment operator are necessary.)

The base class destructor is invoked automatically following the invocation of the derived class destructor. We don't need to invoke it explicitly within the derived class destructor.

    I l@ve RuBoard Previous Section Next Section