15.11 CONSTRUCTOR ORDER DEPENDENCIES IN C


15.11 CONSTRUCTOR ORDER DEPENDENCIES IN C++

Order dependencies in a derived-class constructor refers to the order in which the base-class subobjects are constructed inside a derived-class object; the order in which the specified initializations for the data members of the derived class are carried out; and so on.

To give the reader a sense of what is meant by these order dependencies, consider first the case of the Base class below:

 
//ConstructorOrder.cc #include <iostream> using namespace std; class X { public: X() { cout << "X object under construction" << endl; } }; class Y { public: Y() { cout << "Y object under construction" << endl; } }; class Base { X xobj; //(A) Y yobj; //(B) public: Base() : xobj( X() ), yobj( Y() ) {} //(C) }; int main() { Base b; //(D) return o; }

When this program is executed, object construction in line (D) will call for the initializations of the data members xobj and yobj according to the code in line (C). The two initializations in line (C) will cause the following output to be produced:

      X object under construction      Y object under construction 

That is, in line (C) the data member xobj is initialized before the data member yobj. But if you were to switch the lines (A) and (B) in the program, but leave the code in line (C) unchanged, the following output will be produced:

     Y object under construction     X object under construction 

This example illustrates that if a constructor includes member initialization code for the data members of the class, the data members will be initialized in the order in which they are defined for the class. This order may not be the same order as indicated by the programmer in the member initialization syntax.

This order dependency of a constructor carries over to derived-class constructors. However, now we also have to worry about the order in which the base-class subobjects are constructed in the derived-class object. As we will show in the next chapter, C++ allows for multiple inheritance, meaning that a derived class can have multiple bases.

The official rules for the order in which the code for a derived-class constructor is executed are:

  1. When a derived-class constructor is invoked, first the memory needed for constructing the derived-class object is appropriated.

  2. Next, the constructor of the base class is called to construct the base-class slice of the derived-class object. (If the derived class has multiple bases, the base-class constructor for each base is invoked in the order in which the bases are declared in the header of the derived class, and regardless of the order used by the programmer in his/her coding of the derived-class constructor.)

  3. If the derived-class constructor uses the member initialization syntax for the data member of the derived class, the initializers are invoked in the order in which the data members are declared in the class definition, and regardless of the order they are shown in the member initialization syntax by the programmer.

  4. Finally, the code in the body of the derived-class constructor is executed.

Let's see what the second rule above implies when we try to construct an object of type Manager in the three-class inheritance chain of Figure 15.8. The construction of a Manager object begins with a call to an Employee constructor. But the Employee constructor will in turn call the Person constructor. During this process, the Manager object under construction will change its type identity as each direct or indirect base class constructor is successfully called.[11]

The fact that the type identity of an object under construction changes has an important bearing on which virtual function gets chosen for invocation if the base constructor code invokes such a function. To illustrate this point, both the base class and the derived class in the following example possess a virtual function named foo(). This function is invoked in line (A) during the construction of a base-class object using the no-arg constructor. This no-arg constructor will be called upon to construct the base-class slice of the derived-class object d in line (E). The question now is, Which foo() would be invoked, the base-class's foo() or the derived-class's foo()? On the basis of the explanation provided in the previous paragraph, it is Base's foo().[12]

 
//ConstructorOrderFoo.cc #include <iostream> using namespace std; class Base { public: Base() { foo(); } //(A) virtual void foo() { //(B) cout << "Base's foo invoked" << endl; } }; class Derived : public Base { public: Derived() {} //(C) void foo() { //(D) cout << "Derived's foo invoked" << endl; } }; int main() { Derived d; // invokes Base's foo() //(E) return o; }

[11]The source of this insight is an explanation provided by James Kuyper Jr. in response to a query posted by the author on the comp. std. C++ newsgroup.

[12]As we will see later, Java will select Derived's foo() in a similar situation.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

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