529 Type Conversion in Class Hierarchies This chapter describes implicit type conversion within class hierarchies, which occurs in the context of assignments and function calls. In addition, explicit type casting in class hierarchies is discussed, in particular, upcasting and downcasting. chapter 24 530 ■ CHAPTER 24 TYPE CONVERSION IN CLASS HIERARCHIES ■ CONVERTING TO BASE CLASSES Example for implicit conversion #include "car.h" bool compare( Car&, Car&); int main() { PassCar beetle("New Beetle", false, 3421, "VW"), miata( "Miata", true, 2512, "Mazda"); bool res = compare( beetle, miata); // } // ok! // Implicit conversion // to base class. // Car& a = beetle; // Car& b = miata; bool compare( Car& a, Car& b) { // Here a is the base part of beetle, // b is the base part of miata. // If this is inconvenient, an explicit // type cast to type PassCar has to be performed. } CONVERTING TO BASE CLASSES ■ 531 ᮀ Implicit Conversion If a class is derived from another class by public inheritance, the derived class assumes the characteristics and features of the base class. Objects of the derived class type then become special objects of the base class, just like an automobile is a special type of vehi- cle. You can utilize the is relationship when handling objects. It is possible to assign an object of a derived class to an object of the base class. This causes an implicit type conver- sion to a base class type. The base class thus becomes a generic term for multiple special cases. Given that the classes PassCar and Truck were derived from the Car class, objects of the PassCar or Truck type can always be managed like objects of Car type. ᮀ Assignments Implicit type conversion in class hierarchies occurs in assignments to ■ base class objects ■ pointers or references to the base class. ᮀ Function Calls Additionally, a similar kind of implicit type conversion takes place for the arguments of function calls. Given the function compare() with the following prototype Example: bool compare( Car& , Car& ); and two objects of the derived PassCar class type, beetle and miata, the following statement is valid Example: compare( beetle, miata); The compiler performs implicit type conversion for the arguments beetle and miata, converting them to the parameter type, that is, to a reference to the base class Car. Type conversion for arguments used in function calls is similar to the type conversion that occurs in assignments, as shown in the following section. 532 ■ CHAPTER 24 TYPE CONVERSION IN CLASS HIERARCHIES auto bmw nr: 4325 producer: "Bayer " nr: 4325 sunRoof: true producer: "Bayer " passCarType: "520i" ■ TYPE CONVERSIONS IN ASSIGNMENTS ᮀ Effect of an assignment Car auto; PassCar bmw("520i", true, 4325, "Bayerische Motorenwerke"); auto = bmw; TYPE CONVERSIONS IN ASSIGNMENTS ■ 533 ᮀ Assignment to a Base Class Object An object belonging to a derived class type can be assigned to an object of a base class. Example: Car auto; PassCar bmw("520i", true, 4325, "Bayerische Motorenwerke"); auto = bmw; The object bmw, which belongs to the derived class PassCar, contains all the data members of the base class, Car, i.e. the vehicle id number and the manufacturer. During an assignment the object bmw is copied to the data members of the object auto step by step. This makes the above statement equivalent to: auto.nr = bmw.nr; auto.producer = bmw.producer; The data members additionally defined in the derived class are not copied! The following statement outputs the copied data members: Example: auto.display(); The fact that you can assign an object belonging to a derived class to a base class object assumes that more will always fill less. The object on the right of the assignment operator will always contain a member object of the type on the left of the operator. ᮀ Assignments to Derived Class Objects This is not the case when you attempt to assign a base class object to an object of a derived class. The assignment Example: bmw = auto; // Error! is therefore invalid, since the values for the additional data members passCarType and sunRoof are unknown. An assignment in reverse order is only possible if you have defined an assignment of this type or a copy constructor with a parameter of the type “reference to base class.” Both would be able to supply default values for the additional data members of the derived classes. 534 ■ CHAPTER 24 TYPE CONVERSION IN CLASS HIERARCHIES carPtr cabrio nr producer 1001 "Triumph" carType "Spitfire" sunRoof true ■ CONVERTING REFERENCES AND POINTERS ᮀ Effect of a pointer assignment PassCar cabrio("Spitfire", true, 1001, "Triumph"); Car* carPtr = &cabrio; carPtr = &cabrio; CONVERTING REFERENCES AND POINTERS ■ 535 ᮀ Converting to Base Class Pointers The is relationship between a derived class and a base class is also apparent when refer- ences and pointers are used. A pointer of the type “pointer to base class,” or base class pointer for short, can reference an object of a derived class type. Example: Car* carPtr = &cabrio; In this case cabrio is an object of the class PassCar. The following rule applies for access to the referenced object: ■ a base class pointer can only access the public interface of the base class. The additional members defined in the derived class are therefore inaccessible. To make this more clear: Example: carPtr -> display(); calls the display() method in the base class Car. Although carPtr points to an object of the PassCar class in this case, it is impossible to call any methods additionally defined in the derived class. Example: carPtr->setSunRoof(false); // Error The object *carPtr belongs to the Car class and only represents the generic part of cabrio. Thus, the following assignment is also invalid Example: PassCar auto; auto = *carPtr; // Error! although carPtr is pointing at an object of the PassCar type in this case! ᮀ Conversions in References to Base Classes A similar situation arises when you are working with references. A reference of the type “reference to base class” can point to an object of a derived class. The reference will address only the generic part of the object in this case. Example: Car& carRef = cabrio; // ok carRef.display(); // Output base members carRef.setSunRoof(true); // Error PassCar auto; auto = carRef; // Error Although the reference carRef points to an object of the PassCar type, it is impossi- ble to assign the PassCar type object auto to this object. 536 ■ CHAPTER 24 TYPE CONVERSION IN CLASS HIERARCHIES Car PassCar carPtr static_cast<PassCar*>(carPtr) Pointer to Base class Derived class Downcast Car PassCar static_cast<Car*>(PassCarPtr) PassCarPtr Pointer to Base class Derived class Upcast ■ EXPLICIT TYPE CONVERSIONS Downcast Upcast EXPLICIT TYPE CONVERSIONS ■ 537 ᮀ Upcasts and Downcasts Type conversions that walk up a class hierarchy, or upcasts, are always possible and safe. Upcasting is performed implicitly for this reason. Type conversions that involve walking down the tree, or downcasts, can only be per- formed explicitly by means of a cast construction. The cast operator (type), which was available in C, or the static_cast< > operator are available for this task, and are equivalent in this case. ᮀ Explicit Cast Constructions Given that cabrio is again an object of the derived class PassCar, the following state- ments Example: Car* carPtr = &cabrio; ( (PassCar*) carPtr )->display(); first point the base class pointer carPtr to the cabrio object. carPtr is then cast as a pointer to the derived class. This allows you to access the display() method of the derived class PassCar via the pointer. Parentheses are necessary in this case as the member access operator -> has a higher precedence than the cast operator (type). The operator static_cast< > conforms to the following Syntax: static_cast<type>(expression) and converts the expression to the target type type. The previous example is thus equiv- alent to Example: static_cast<PassCar*>(carPtr)->display(); No parentheses are required here as the operators static_cast<> and -> are of equal precedence. They are read from left to right. After downcasting a pointer or a reference, the entire public interface of the derived class is accessible. ᮀ Downcast Safety Issues Type conversions from top to bottom need to be performed with great care. Downcasting is only safe when the object referenced by the base class pointer really is a derived class type. This also applies to references to base classes. To allow safe downcasting C++ introduces the concept of dynamic casting. This tech- nique is available for polymorphic classes and will be introduced in the next chapter. exercise 538 ■ CHAPTER 24 TYPE CONVERSION IN CLASS HIERARCHIES Product PrepackedFood FreshFood ■ EXERCISE Class hierarchy of products in a supermarket . CONVERSION IN CLASS HIERARCHIES Car PassCar carPtr static_cast<PassCar*>(carPtr) Pointer to Base class Derived class Downcast Car PassCar static_cast<Car*>(PassCarPtr) PassCarPtr Pointer to Base. generic part of cabrio. Thus, the following assignment is also invalid Example: PassCar auto; auto = *carPtr; // Error! although carPtr is pointing at an object of the PassCar type in this case! ᮀ. of a cast construction. The cast operator (type), which was available in C, or the static_cast< > operator are available for this task, and are equivalent in this case. ᮀ Explicit Cast