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 possible 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 discounts 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 the size of the discount. Now 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 virtual 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 1 and 2 contain the interface and implement for the class Sale.
Display 1—Interface for the Base Class Sale
//This is the header file sale.h.
//This is the interface for the class Sale.
//Sale is a class for simple sales.
#ifndef SALE_H
#define SALE_H
namespace SavitchSale
{
class Sale
{
public:
Sale( );
Sale(double thePrice);
double getPrice( ) const;
void setPrice(double newPrice);
virtual double bill( ) const;
double savings(const Sale& other) const;
//Returns the savings if you buy other instead of the calling object.
private:
double price;
};
bool operator < (const Sale& first, const Sale& second);
//Compares two sales to see which is larger.
}//SavitchSale
#endif // SALE_H
Display 2—Implementation of the Base Class Sale
//This is the file sale.cpp.
//This is the implementation for the class Sale.
//The interface for the class Sale is in the file sale.h.
#include <iostream>
#include "sale.h"
using std::cout;
namespace SavitchSale
{
Sale::Sale( ) : price(0)
{
//Intentionally empty
}
Sale::Sale(double thePrice)
{
if (thePrice >= 0)
price = thePrice;
else
{
cout << "Error: Cannot have a negative price!
";
exit(1);
}
}
double Sale::bill( ) const
{
return price;
}
double Sale::getPrice( ) const
{
return price;
}
void Sale::setPrice(double newPrice)
{
if (newPrice >= 0)
price = newPrice;
else
{
cout << "Error: Cannot have a negative price!
";
exit(1);
}
}
double Sale::savings(const Sale& other) const
{
return (bill( ) - other.bill( ));
}
bool operator < (const Sale& first, const Sale& second)
{
return (first.bill( ) < second.bill( ));
}
}//SavitchSale
For example, Displays 3 and 4 show the derived class DiscountSale.
Display 3—Interface for the Derived Class DiscountSale
//This is the file discountsale.h.
//This is the interface for the class DiscountSale.
#ifndef DISCOUNTSALE_H
#define DISCOUNTSALE_H
#include "sale.h"
namespace SavitchSale
{
class DiscountSale : public Sale
{
public:
DiscountSale( );
DiscountSale(double thePrice, double theDiscount);
//Discount is expressed as a percent of the price.
//A negative discount is a price increase.
double getDiscount( ) const;
void setDiscount(double newDiscount);
double bill( ) const;
private:
double discount;
};
}//SavitchSale
#endif //DISCOUNTSALE_H
Display 4—Implementation for the Derived Class DiscountSale
//This is the implementation for the class DiscountSale.
//This is the file discountsale.cpp.
//The interface for the class DiscountSale is in the header file discountsale.h.
#include "discountsale.h"
namespace SavitchSale
{
DiscountSale::DiscountSale( ) : Sale( ), discount(0)
{
//Intentionally empty
}
DiscountSale::DiscountSale(double thePrice, double theDiscount)
: Sale(thePrice), discount(theDiscount)
{
//Intentionally empty
}
double DiscountSale::getDiscount( ) const
{
return discount;
}
void DiscountSale::setDiscount(double newDiscount)
{
discount = newDiscount;
}
double DiscountSale::bill( ) const
{
double fraction = discount/100;
return (1 - fraction)*getPrice( );
}
}//SavitchSale
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 5 gives a sample program that illustrates the virtual function.
Display 5—Use of a Virtual Function
//Demonstrates the performance of the virtual function bill.
#include <iostream>
#include "sale.h" //Not really needed, but safe due to ifndef.
#include "discountsale.h"
using std::cout;
using std::endl;
using std::ios;
using namespace SavitchSale;
int main( )
{
Sale simple(10.00);//One item at $10.00.
DiscountSale discount(11.00, 10);//One item at $11.00 with a 10% discount.
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
if (discount < simple)
{
cout << "Discounted item is cheaper.
";
cout << "Savings is ___FCKpd___4quot; << simple.savings(discount) << endl;
}
else
cout << "Discounted item is not cheaper.
";
return 0;
}
//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.
Sample Dialogue
Discounted item is cheaper.
Savings is $0.10