15 Polymorphism and Virtual Functions 15.1 VIRTUAL FUNCTION BASICS 628 Late Binding 628 Virtual Functions in C++ 629 Tip: The Virtual Property Is Inherited 636 Tip: When to Use a Virtual Function 636 Pitfall: Omitting the Definition of a Virtual Member Function 637 Abstract Classes and Pure Virtual Functions 637 Example: An Abstract Class 638 15.2 POINTERS AND VIRTUAL FUNCTIONS 641 Virtual Functions and Extended Type Compatibility 641 Pitfall: The Slicing Problem 645 Tip: Make Destructors Virtual 646 Downcasting and Upcasting 647 How C++ Implements Virtual Functions 649 CHAPTER SUMMARY 650 ANSWERS TO SELF-TEST EXERCISES 651 PROGRAMMING PROJECTS 651 15 Polymorphism and Virtual Functions I did it my way. Frank Sinatra INTRODUCTION Polymorphism refers to the ability to associate many meanings to one function name by means of a special mechanism known as virtual functions or late bind- ing . Polymorphism is one of the fundamental mechanisms of a popular and powerful programming philosophy known as object-oriented programming . Wow, lots of fancy words! This chapter will explain them. Section 15.1 does not require the material from Chapters 10 (pointers and dynamic arrays), 12 (file I/O), or 13 (recursion). Section 15.2 does not require the material from Chapters 12 (file I/O) or 13 (recursion), but does require the material from Chapter 10 (pointers and dynamic arrays). Virtual Function Basics virtual adj. 1. Existing or resulting in essence or effect though not in actual fact, form, or name. The American Heritage Dictionary of the English Language, Third Edition A virtual function is so named because it may, in a sense to be made clear, be used before it is defined. Virtual functions will prove to be another tool for software reuse. ■ LATE BINDING Virtual functions are best explained by an example. Suppose you are designing software for a graphics package that has classes for several kinds of figures, such as rectangles, circles, ovals, and so forth. Each figure might be an object of a different class. For example, the Rectangle class might have member vari- ables for a height, width, and center point, while the Circle class might have member variables for a center point and a radius. In a well-designed program- ming project, all of these classes would probably be descendants of a single parent class called, for example, Figure . Now, suppose you want a function to draw a figure on the screen. To draw a circle, you need different instructions from those you need to draw a rectangle. So, each class needs to have a differ- ent function to draw its kind of figure. However, because the functions belong 15.1 Virtual Function Basics 629 to the classes, they can all be called draw . If r is a Rectangle object and c is a Circle object, then r.draw( ) and c.draw( ) can be functions implemented with different code. All this is not news, but now we move on to something new: virtual functions defined in the parent class Figure . The parent class Figure may have functions that apply to all figures. For example, it might have a function called center that moves a figure to the center of the screen by erasing it and then redrawing it in the center of the screen. The function Figure::cen- ter might use the function draw to redraw the figure in the center of the screen. When you think of using the inherited function center with figures of the classes Rectangle and Circle , you begin to see that there are complications here. To make the point clear and more dramatic, let’s suppose that the class Figure is already written and in use and that at some later time you add a class for a brand-new kind of figure, say, the class Triangle . Now, Triangle can be a derived class of the class Figure , and so the function center will be inherited from the class Figure . The func- tion center should therefore apply to (and perform correctly for) all Triangle s. But there is a complication. The function center uses draw , and the function draw is differ- ent for each type of figure. The inherited function center (if nothing special is done) will use the definition of the function draw given in the class Figure , and that function draw does not work correctly for Triangle s. We want the inherited member function center to use the function Triangle::draw rather than the function Figure::draw . But the class Triangle —and therefore the function Triangle::draw —was not even written when the function center (defined in the class Figure ) was written and com- piled! How can the function center possibly work correctly for Triangle s? The com- piler did not know anything about Triangle::draw at the time that center was compiled! The answer is that it can apply provided draw is a virtual function . When you make a function virtual , you are telling the compiler “I do not know how this function is implemented. Wait until it is used in a program, and then get the implementation from the object instance.” The technique of waiting until runtime to determine the implementation of a function is often called late binding or dynamic binding . Virtual functions are the way C++ provides late binding. But enough intro- duction. We need an example to make this come alive (and to teach you how to use vir- tual functions in your programs). To explain the details of virtual functions in C++, we will use a simplified example from an application area other than drawing figures. ■ VIRTUAL FUNCTIONS IN C++ Suppose you are designing a record-keeping program for an automobile parts store. You want to make the program versatile, but you are not sure you can account for all possi- ble situations. For example, you want to keep track of sales, but you cannot anticipate all types of sales. At first, there will only be regular sales to retail customers who go to the store to buy one particular part. However, later you may want to add sales with dis- counts or mail order sales with a shipping charge. All these sales will be for an item with a basic price and ultimately will produce some bill. For a simple sale, the bill is just the basic price, but if you later add discounts, then some kinds of bills will also depend on virtual function late binding or dynamic binding 630 Polymorphism and Virtual Functions the size of the discount. Your program will need to compute daily gross sales, which intuitively should just be the sum of all the individual sales bills. You may also want to calculate the largest and smallest sales of the day or the average sale for the day. All these can be calculated from the individual bills, but many of the functions for computing the bills will not be added until later, when you decide what types of sales you will be dealing with. To accommodate this, we make the function for computing the bill a vir- tual function. (For simplicity in this first example, we assume that each sale is for just one item, although with derived classes and virtual functions we could, but will not here, account for sales of multiple items.) Displays 15.1 and 15.2 contain the interface and implementation for the class Sale . All types of sales will be derived classes of the class Sale . The class Sale corresponds to Display 15.1 Interface for the Base Class Sale 1 2 //This is the header file sale.h. 3 //This is the interface for the class Sale. 4 //Sale is a class for simple sales. 5 #ifndef SALE_H 6 #define SALE_H 7 namespace SavitchSale 8 { 9 class Sale 10 { 11 public: 12 Sale( ); 13 Sale(double thePrice); 14 double getPrice( ) const; 15 void setPrice(double newPrice); 16 virtual double bill( ) const; 17 double savings(const Sale& other) const; 18 //Returns the savings if you buy other instead of the calling object. 19 private: 20 double price; 21 }; 22 bool operator < (const Sale& first, const Sale& second); 23 //Compares two sales to see which is larger. 24 }//SavitchSale 25 #endif // SALE_H Virtual Function Basics 631 Display 15.2 Implementation of the Base Class Sale (part 1 of 2) 1 2 //This is the file sale.cpp. 3 //This is the implementation for the class Sale. 4 //The interface for the class Sale is in the file sale.h. 5 #include <iostream> 6 #include "sale.h" 7 using std::cout; 8 namespace SavitchSale 9 { 10 Sale::Sale( ) : price(0) 11 { 12 //Intentionally empty 13 } 14 Sale::Sale(double thePrice) 15 { 16 if (thePrice >= 0) 17 price = thePrice; 18 else 19 { 20 cout << "Error: Cannot have a negative price!\n"; 21 exit(1); 22 } 23 } 24 double Sale::bill( ) const 25 { 26 return price; 27 } 28 double Sale::getPrice( ) const 29 { 30 return price; 31 } 32 33 void Sale::setPrice(double newPrice) 34 { 35 if (newPrice >= 0) 36 price = newPrice; 37 else 38 { 39 cout << "Error: Cannot have a negative price!\n"; 40 exit(1); 632 Polymorphism and Virtual Functions simple sales of a single item with no added discounts or charges. Notice the reserved word virtual in the declaration for the member function bill (Display 15.1). Notice (Display 15.2) that the member function savings and the overloaded operator, <, each use the function bill. Since bill is declared to be a virtual function, we can later define derived classes of the class Sale and define their versions of the member function bill, and the definitions of the member function savings and the overloaded operator, <, which we gave with the class Sale, will use the version of the member function bill that corresponds to the object of the derived class. For example, Displays 15.3 and 15.4 show the derived class DiscountSale. Notice that the class DiscountSale requires a different definition for its version of the member function bill. Nonetheless, when the member function savings and the overloaded operator, <, are used with an object of the class DiscountSale, they will use the version of the function definition for bill that was given with the class DiscountSale. This is indeed a pretty fancy trick for C++ to pull off. Consider the function call d1.sav- ings(d2) for objects d1 and d2 of the class DiscountSale. The definition of the function savings (even for an object of the class DiscountSale) is given in the implementation file for the base class Sale, which was compiled before we even thought of the class DiscountSale. Yet, in the function call d1.savings(d2), the line that calls the function bill knows enough to use the definition of the function bill given for the class DiscountSale. How does this work? In order to write C++ programs you can just assume it happens by magic, but the real explanation was given in the introduction to this section. When you label a function virtual, you are telling the C++ environment “Wait until this function is used in a program, and then get the implementation corresponding to the calling object.” Display 15.5 gives a sample program that illustrates how the virtual function bill and the functions that use bill work in a complete program. Display 15.2 Implementation of the Base Class Sale (part 2 of 2) 41 } 42 } 43 double Sale::savings(const Sale& other) const 44 { 45 return (bill( ) - other.bill( )); 46 } 47 bool operator < (const Sale& first, const Sale& second) 48 { 49 return (first.bill( ) < second.bill( )); 50 } 51 }//SavitchSale Virtual Function Basics 633 Display 15.3 Interface for the Derived Class DiscountSale 1 2 //This is the file discountsale.h. 3 //This is the interface for the class DiscountSale. 4 #ifndef DISCOUNTSALE_H 5 #define DISCOUNTSALE_H 6 #include "sale.h" 7 namespace SavitchSale 8 { 9 class DiscountSale : public Sale 10 { 11 public: 12 DiscountSale( ); 13 DiscountSale(double thePrice, double theDiscount); 14 //Discount is expressed as a percentage of the price. 15 //A negative discount is a price increase. 16 double getDiscount( ) const; 17 void setDiscount(double newDiscount); 18 double bill( ) const; 19 private: 20 double discount; 21 }; 22 }//SavitchSale 23 #endif //DISCOUNTSALE_H Since bill was declared virtual in the base class, it is automatically virtual in the derived class DiscountSale. You can add the modifier virtual to the declaration of bill or omit it as here; in either case bill is virtual in the class DiscountSale. (We prefer to include the word virtual in all virtual function declarations, even if it is not required. We omitted it here to illustrate that it is not required.) Display 15.4 Implementation for the Derived Class DiscountSale (part 1 of 2) 1 2 //This is the implementation for the class DiscountSale. 3 //This is the file discountsale.cpp. 4 //The interface for the class DiscountSale is in the header file 5 //discountsale.h. 6 #include "discountsale.h" 7 namespace SavitchSale 8 { 9 DiscountSale::DiscountSale( ) : Sale( ), discount(0) 10 { 634 Polymorphism and Virtual Functions Display 15.4 Implementation for the Derived Class DiscountSale (part 2 of 2) 11 //Intentionally empty 12 } 13 DiscountSale::DiscountSale(double thePrice, double theDiscount) 14 : Sale(thePrice), discount(theDiscount) 15 { 16 //Intentionally empty 17 } 18 double DiscountSale::getDiscount( ) const 19 { 20 return discount; 21 } 22 void DiscountSale::setDiscount(double newDiscount) 23 { 24 discount = newDiscount; 25 } 26 double DiscountSale::bill( ) const 27 { 28 double fraction = discount/100; 29 return (1 - fraction)*getPrice( ); 30 } 31 }//SavitchSale You do not repeat the qualifier virtual in the function definition. Display 15.5 Use of a Virtual Function (part 1 of 2) 1 2 //Demonstrates the performance of the virtual function bill. 3 #include <iostream> 4 #include "sale.h" //Not really needed, but safe due to ifndef. 5 #include "discountsale.h" 6 using std::cout; 7 using std::endl; 8 using std::ios; 9 using namespace SavitchSale; 10 int main( ) 11 { Virtual Function Basics 635 V IRTUAL F UNCTION A virtual function is indicated by including the modifier virtual in the member function decla- ration (which is given in the definition of the class). If a function is virtual and a new definition of the function is given in a derived class, then for any object of the derived class, that object will always use the definition of the virtual function that was given in the derived class, even if the virtual function is used indirectly by being invoked in the definition of an inherited function. This method of deciding which definition of a virtual func- tion to use is known as late binding. P OLYMORPHISM PP PP oo oo ll ll yy yy mm mm oo oo rr rr pp pp hh hh ii ii ss ss mm mm refers to the ability to associate many meanings to one function name by means of the late-binding mechanism. Thus, polymorphism, late binding, and virtual functions are really all the same topic. Display 15.5 Use of a Virtual Function (part 2 of 2) 12 Sale simple(10.00);//One item at $10.00. 13 DiscountSale discount(11.00, 10);//One item at $11.00 with a 10% discount. 14 cout.setf(ios::fixed); 15 cout.setf(ios::showpoint); 16 cout.precision(2); 17 if (discount < simple) 18 { 19 cout << "Discounted item is cheaper.\n"; 20 cout << "Savings is $" << simple.savings(discount) << endl; 21 } 22 else 23 cout << "Discounted item is not cheaper.\n"; 24 return 0; 25 } S AMPLE D IALOGUE Discounted item is cheaper. Savings is $0.10 The objects discount and simple use different code for the member function bill when the less-than comparison is made. Similar remarks apply to savings. 636 Polymorphism and Virtual Functions Tip Tip T HE V IRTUAL P ROPERTY I S I NHERITED The property of being a virtual function is inherited. For example, since bill was declared to be virtual in the base class Sale (Display 15.1), the function bill is automatically virtual in the derived class DiscountSale (Display 15.3). So, the following two declarations of the member function bill would be equivalent in the definition of the derived class DiscountSale: double bill( ) const; virtual double bill( ) const; Thus, if SuperDiscountSale is a derived class of the class DiscountSale that inherits the function savings, and if the function bill is given a new definition for the class SuperDis- countSale , then all objects of the class SuperDiscountSale will use the definition of the function bill given in the definition of the class SuperDiscountSale. Even the inherited func- tion savings (which includes a call to the function bill) will use the definition of bill given in SuperDiscountSale whenever the calling object is in the class SuperDiscountSale. W HEN TO U SE A V IRTUAL F UNCTION There are clear advantages to using virtual functions and no clear disadvantages that we have seen so far. So, why not make all member functions virtual? In fact, why not define the C++ com- piler so that (like some other languages, such as Java) all member functions are automatically vir- tual? The answer is that there is a large overhead to making a function virtual. Doing so uses more storage and makes your program run slower than if the function were not virtual. That is why the designers of C++ gave the programmer control over which member functions are virtual and which are not. If you expect to need the advantages of a virtual member function, then make that mem- ber function virtual. If you do not expect to need the advantages of a virtual function, then your program will run more efficiently if you do not make the member function virtual. O VERRIDING When a virtual function definition is changed in a derived class, programmers often say the function definition is overridden. In the C++ literature a distinction is usually made between the terms redefined and overridden . Both terms refer to changing the definition of the function in a derived class. If the function is a virtual function, this act is called overriding . If the function is not a virtual function, it’s called redefining . This may seem like a silly distinction to you the pro- grammer, since you do the same thing in both cases, but the two cases are treated differently by the compiler. . 628 Virtual Functions in C++ 629 Tip: The Virtual Property Is Inherited 636 Tip: When to Use a Virtual Function 636 Pitfall: Omitting the Definition of a Virtual Member Function 637 Abstract Classes. in C++, we will use a simplified example from an application area other than drawing figures. ■ VIRTUAL FUNCTIONS IN C++ Suppose you are designing a record-keeping program for an automobile parts. Definition of a Virtual Member Function 637 Abstract Classes and Pure Virtual Functions 637 Example: An Abstract Class 638 15.2 POINTERS AND VIRTUAL FUNCTIONS 641 Virtual Functions and Extended Type