10.5. Constructors in Derived ClassesAs 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 the MyBase reference) or implicitly (calling the base class's default or parameterless constructor). Similarly, if the base class is derived from another class (as is every class except Object), the base class constructor invokes the constructor of the next class up the hierarchy, and so on. The last constructor called in the chain is always the constructor for class Object. The original derived class constructor's body finishes executing last. Each base class's constructor manipulates the base class instance variables that the derived class object inherits. For example, let's reconsider the CommissionEmployeeBasePlusCommissionEmployee hierarchy from Fig. 10.13 and 10.14. When a program creates a BasePlusCommissionEmployee object, the BasePlusCommissionEmployee constructor is called. That constructor, before executing its full body code, immediately calls CommissionEmployee's constructor, which in turn calls Object's constructor. Class Object's constructor has an empty body, so it immediately returns control to the CommissionEmployee's constructor, which then initializes the Private instance variables of CommissionEmployee that are part of the BasePlusCommissionEmployee object. When the CommissionEmployee's constructor completes execution, it returns control to the BasePlusCommissionEmployee's constructor, which initializes the BasePlusCommissionEmployee object's baseSalaryValue. Our next example revisits the commission employee hierarchy by redeclaring class CommissionEmployee (Fig. 10.16) and class BasePlusCommissionEmployee (Fig. 10.17) with each class's constructor printing a message when invoked, enabling us to observe the order in which the constructors in the hierarchy execute. Figure 10.16. CommissionEmployee's constructor outputs text.
Figure 10.17. BasePlusCommissionEmployee's constructor outputs text.
Class CommissionEmployee (Fig. 10.16) contains the same features as the version of the class shown in Fig. 10.13. We have modified the constructor (lines 1122) to output text upon its invocation (lines 2021). Note that outputting Me with the {0} format string (line 21) implicitly invokes the ToString method of the CommissionEmployee object being constructed to obtain the object's string representation. Class BasePlusCommissionEmployee (Fig. 10.17) is almost identical to Fig. 10.14, except that BasePlusCommissionEmployee's constructor also outputs text when invoked (lines 1617). Again, we output Me using the {0} format string (line 17) to implicitly invoke the ToString method, this time to obtain the BasePlusCommissionEmployee object's string representation. Figure 10.18 demonstrates the order in which constructors are called for objects of classes that are part of an inheritance hierarchy. Method Main begins by instantiating CommissionEmployee object employee1 (lines 67). Next, lines 1011 instantiate BasePlusCommissionEmployee object employee2. This invokes the CommissionEmployee constructor, which prints output with the values passed from the BasePlusCommissionEmployee constructor, then performs the output specified in the BasePlusCommissionEmployee constructor. Figure 10.18. Constructor call order using Me.
Analyzing the OutputThe outputs of the CommissionEmployee constructor and BasePlusCommissionEmployee constructor calls each contain values for the first name, last name, social security number, gross sales, commission rate and base salary of the BasePlusCommissionEmployee. When constructing a BasePlusCommissionEmployee object, the Me reference used in the body of both the CommissionEmployee and BasePlusCommissionEmployee constructors refers to the BasePlusCommissionEmployee object being constructed. When a program invokes method ToString on an object, the version of ToString that executes is always the version defined in that object's class. This is an example of polymorphism, a key aspect of object-oriented programming that we discuss in detail in Chapter 11. Reference Me refers to the current BasePlusCommissionEmployee object being constructed, so BasePlusCommissionEmployee's ToString method executes even when ToString is invoked from the body of class CommissionEmployee's constructor. This would not be the case if the CommissionEmployee constructor were called to initialize a new CommissionEmployee object. When the CommissionEmployee constructor invokes method ToString for the BasePlusCommissionEmployee being constructed, the program displays 0 for the BaseSalary value, because the BasePlusCommissionEmployee constructor's body has not yet initialized the BaseSalary. The BasePlusCommissionEmployee constructor output shows the proper BaseSalary value (i.e., 800.00), because this line is output after the BaseSalary is initialized. Using MyClass in Class CommissionEmployee's ConstructorTo force CommissionEmployee's ToString method to execute when the CommissionEmployee's constructor is called, we use the MyClass reference. Reference MyClass is similar to Me, except that a method call with MyClass always invokes the version of the method defined in that particular classthe method called is not affected by the runtime type of the object. For example, if we replace Me in line 21 of Fig. 10.16 with MyClass.ToString() then CommissionEmployee's ToString will be executed (even though the runtime type of the object is BasePlusCommissionEmployee). Thus, the output of the CommissionEmployee's constructor call will not display the base salary value (Fig. 10.19).
Recall that Shared class variables and Shared class methods exist independently of any class objects. They also exist when there are no objects of that class. Hence, the MyClass reference cannot be used in a Shared method because such a method can be called even when there are no objects of the corresponding class. |