1. Trang chủ
  2. » Công Nghệ Thông Tin

A Complete Guide to Programming in C++ part 57 pps

10 222 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Nội dung

EXERCISE ■ 539 Exercise The class hierarchy representing a supermarket chain’s checkout system comprises the base class Product and the derived classes PrepackedFood and FreshFood.Your job is to test various cast techniques for this class (see also Exercise 3 in Chapter 23). ■ Define a global function isLowerCode() that determines which one of two products has the lower barcode and returns a reference to the prod- uct with the lower barcode. ■ Define an array with three pointers to the base class Product. Dynami- cally create one object each of the types Product, PrepackedFood, and FreshFood.The three objects are to be referenced by the array pointers. Additionally define a pointer to the derived class FreshFood. Initialize the pointer with the address of a dynamically allocated object of the same class. ■ Now call the method printer() for all four objects.Which version of printer() is executed? ■ Perform downcasting to execute the correct version of printer() in every case. Display the pointer values before and after downcasting. ■ Use the pointer of the derived class FreshFood to call the base class ver- sion of printer(). Perform an appropriate upcast. ■ Test the function isLowerCode()by multiple calls to the function with various arguments. Output the product with the lower barcode value in each case. solution 540 ■ CHAPTER 24 TYPE CONVERSION IN CLASS HIERARCHIES ■ SOLUTION // // product.h : Defines the classes // Product, PrepackedFood, and FreshFood // // // Unchanged! See the previous chapter's solutions. // // produc_t.cpp // Tests up and down casts for the classes // Product, PrepackedFood, and FreshFood. // #include "product.h" const Product& isLowerCode(const Product& p1, const Product& p2); int main() { Product* pv[3]; FreshFood* pu; pv[0] = new Product(12345L, "Flour"); pv[1] = new PrepackedFood(0.49, 23456, "Salt"); pv[2] = new FreshFood(1.5, 1.69, 98765, "Grapes"); pu = new FreshFood(2.5, 2.69, 56789, "Peaches"); cout << "\nA fresh product: "; pu->printer(); cout << "\nThe generic data of the other products:"; int i; for(i=0; i < 3; ++i) pv[i]->printer(); cin.get(); cout << "\nAnd now the downcast: " << endl; static_cast<PrepackedFood*>(pv[1])->printer(); static_cast<FreshFood*>(pv[2])->printer(); cin.get(); cout << "\nAnd an upcast: " << endl; static_cast<Product*>(pu)->printer(); SOLUTION ■ 541 cout << "\nNow compare the barcodes!" << endl; cout << "\nIs barcode for flour or salt smaller?"; isLowerCode(*pv[0], *pv[1]).printer(); cout << "\nIs barcode for salt or grapes smaller?"; isLowerCode(*pv[1], *pv[2]).printer(); return 0; } const Product& isLowerCode(const Product& p1, const Product& p2) { if(p1.getCode() < p2.getCode()) return p1; else return p2; } This page intentionally left blank 543 Polymorphism This chapter describes how to develop and manage polymorphic classes. In addition to defining virtual functions, dynamic downcasting in polymorphic class hierarchies is introduced. chapter 25 544 ■ CHAPTER 25 POLYMORPHISM Classes with virtual methods: Base class pointer and objects: Calling virtual methods: base class with virtual method display(). derived from Base with its own redefinitions of method display(). When a virtual method is called, the corresponding version of the method is executed for the object currently referenced. Base: Derived1 and Derived2: Base* basePtr; // Base class pointer Derived1 angular; // Objects // Calling // Derived1::display() // Calling // Derived2::display() Derived2 round; basePtr = &angular; basePtr->display(); basePtr = &round; basePtr->display(); ■ CONCEPT OF POLYMORPHISM Example CONCEPT OF POLYMORPHISM ■ 545 ᮀ Issues If the special features of derived class objects are insignificant, you can simply concern yourself with the base members. This is the case when dynamic allocated objects are inserted into a data structure or deleted from that structure. It makes sense to use pointers or references to the base class in this case—no matter what type of concrete object you are dealing with. However, you can only access the common base members of these objects. However, you should be able to activate the special features of a derived class when ■ the object is accessed by a pointer or reference to the base class and ■ the concrete object type will not be known until the program is executed. Given a base class pointer, carPtr, the statement Example: carPtr->display(); should output all the data members of the object currently being referenced. ᮀ Traditional Approach Traditional programming languages solved this issue by adding a type field both to the base class and to the derived classes. The type field stored the type of the current class. A function that manages objects via the base class pointer could query the concrete type in a switch statement and call the appropriate method. This solution has a disadvantage; adding derived classes at a later stage also meant adding a case label and recompiling. ᮀ Object-Oriented Approach The approach adopted by object-oriented languages is polymorphism (Greek for multi- form). In C++, virtual methods are used to implement polymorphic classes. Calling a vir- tual method makes the compiler execute a version of the method suitable for the object in question, when the object is accessed by a pointer or a reference to the base class! 546 ■ CHAPTER 25 POLYMORPHISM // virtual.cpp : Tests the virtual method display() // of the classes Car and PassCar. // #include "car.h" // The Car class with virtual method display(): // class Car // { // // virtual void display() const; // }; int main() { Car* pCar[3]; // Three pointers to the base class. int i = 0; // Index pCar[0] = new Car( 5634L, "Mercedes"); pCar[1] = new PassCar("Miata",true,3421,"Mazda"); pCar[2] = new Truck( 5, 7.5, 1234, "Ford"); while( true) { cout << "\nTo output an object of type " "Car, PassCar or Truck!" "\n 1 = Car, 2 = PassCar, 3 = Truck" "\nYour input (break with 0): "; cin >> i; i; if( i < 0 || i > 2) break; pCar[i]->display(); } return 0; } ■ VIRTUAL METHODS Calling the virtual method display() VIRTUAL METHODS ■ 547 ᮀ Declaring Virtual Methods The virtual keyword is used to declare a virtual method in a base class. Example: virtual void display() const; The definition of a virtual method is no different from the definition of any other mem- ber function. A virtual method does not need to be redefined in the derived class. The derived class then inherits the virtual method from the base class. ᮀ Redefinition However, it is common practice for the derived class to define its own version of the vir- tual method, which is thus modified to suit the special features of the derived class. Creating a proprietary version of a virtual method means redefining that method. The redefinition in the derived class must have 1. the same signature and 2. the same return type as the virtual method in the base class. The new version of a virtual method is automatically virtual itself. This means you can omit the virtual keyword in the declaration. When you redefine a virtual function, be aware of the following: ■ if the return type is a pointer or reference to the base class, the new version of the virtual method can also return a pointer or reference to a derived class (Note: Not all compilers support this option.) ■ constructors cannot have a virtual declaration ■ a base class method does not become virtual just because it is declared as virtual in a derived class. If you use a different signature or return type of a virtual base class method to define a method in a derived class, this simply creates a new method with the same name. The method will not necessarily be virtual! However, the virtual method in the base class will be masked by the method in the derived class. In other words, only the non-virtual version of the method is available for a derived class object. 548 ■ CHAPTER 25 POLYMORPHISM // v_destr.cpp // Base class with a virtual destructor. // #include <iostream> #include <cstring> // For strcpy() using namespace std; class Base { public: Base() { cout << "Constructor of class Base\n"; } virtual ~Base() { cout << "Destructor of class Base\n"; } }; class Data { private: char *name; public: Data( const char *n) { cout << "Constructor of class Data\n"; name = new char[strlen(n)+1]; strcpy(name, n); } ~Data() { cout << "Destructor of class Data for " << "object: " << name << endl; delete [] name; } }; class Derived : public Base { private: Data data; public: Derived( const char *n) : data(n) { cout << "Constructor of class Derived\n"; } ~Derived() // implicit virtual { cout << "Destructor of class Derived\n"; } }; int main() { Base *bPtr = new Derived("DEMO"); cout << "\nCall to the virtual Destructor!\n"; delete bPtr; return 0; } ■ DESTROYING DYNAMICALLY ALLOCATED OBJECTS Sample program . that manages objects via the base class pointer could query the concrete type in a switch statement and call the appropriate method. This solution has a disadvantage; adding derived classes at. by the array pointers. Additionally define a pointer to the derived class FreshFood. Initialize the pointer with the address of a dynamically allocated object of the same class. ■ Now call the. classes at a later stage also meant adding a case label and recompiling. ᮀ Object-Oriented Approach The approach adopted by object-oriented languages is polymorphism (Greek for multi- form). In C++,

Ngày đăng: 06/07/2014, 17:21

TỪ KHÓA LIÊN QUAN