However, you should be able to activate the special features of a derived class when Example: carPtr->display; should output all the data members of the object currently being referenced
Trang 1E X E R C I S E 539
Exercise
The class hierarchy representing a supermarket chain’s checkout system
comprises the base class Productand the derived classes PrepackedFoodand
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 FreshFoodto 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
Trang 2540 C H A P T E R 2 4 T Y P E C O N V E R S I O N I N C L A S S H I E R A R C H I E S
■ 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();
Trang 3S O L U T I O N 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;
}
Trang 4This page intentionally left blank
Trang 55 4 3
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
Trang 6544 C H A P T E R 2 5 P O L Y M O R P H I S M
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
Trang 7C O N C E P T O F P O L Y M O R P H I S M 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
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
䊐 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!
Trang 8546 C H A P T E R 2 5 P O L Y M O R P H I S M
// 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()
Trang 9V I R T U A L M E T H O D S 547
䊐 Declaring Virtual Methods
Thevirtualkeyword 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
When you redefine a virtual function, be aware of the following:
virtual method can also return a pointer or reference to a derived class (Note: Not all compilers support this option.)
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
Trang 10548 C H A P T E R 2 5 P O L Y M O R P H I S M
// 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";
}
~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