0% found this document useful (0 votes)
9 views

Lec11. Polymorphism and Virtual Functions

The document discusses polymorphism and virtual functions in C++. It explains how virtual functions allow derived classes to override base class functions, providing late binding so the correct version runs based on the object. It shows an example using a base Sale class and derived DiscountSale class, where the virtual bill() function behaves differently in each class.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views

Lec11. Polymorphism and Virtual Functions

The document discusses polymorphism and virtual functions in C++. It explains how virtual functions allow derived classes to override base class functions, providing late binding so the correct version runs based on the object. It shows an example using a base Sale class and derived DiscountSale class, where the virtual bill() function behaves differently in each class.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 17

Chapter 15

Polymorphism and
Virtual Functions

Copyright © 2017 Pearson Education, Ltd.


All rights reserved.
Virtual Function Basics
• Polymorphism: Associating many meanings to one function
– Virtual functions provide this capability
– Fundamental principle of object-oriented programming!
• Virtual: Existing in "essence" though not in fact
• Virtual Function: Can be "used" before it’s "defined"

Copyright © 2017 Pearson Education, Ltd. All rights reserved. 15-2


Best explained by example
• Assume a base class Figure
– Classes for several kinds of figures can be derived from parent Figure
• Rectangles, circles, ovals, etc.
• Each figure might be an object of different class
– Rectangle data: height, width, center point
– Circle data: center point, radius

• Suppose you want to draw a figure using function: draw()


– Different instructions for each figure
– Each class needs different draw function
Rectangle r;
– Can be called "draw" in each class, so: Circle c;
r.draw(); //Calls Rectangle class’s draw
– Nothing new here yet…
c.draw(); //Calls Circle class’s draw
• Figure may contain functions that apply to "all" figures; consider:
– center(): moves a figure to center of screen
• Erases 1st, then re-draws, so Figure::center() will use function draw() to
re-draw
15-3
– Which draw() function and from which class?
Best explained by example: New Figure
• Consider new kind of figure (Triangle class derived from
Figure)
• Function center() inherited from Figure
– Will it work for triangles?
– It uses draw(), which is different for each figure!
– It will use Figure::draw()  won’t work for triangles
– We want inherited function center()to use function
Triangle::draw() NOT function Figure::draw()
• But class Triangle wasn’t even WRITTEN when Figure::center() was!
– It Doesn’t know "triangles"!

• Answer: Virtual functions. They implement late/dynamic binding


– They tell compiler:
• "Don’t know how function is implemented.”
• “ Wait until used in program then get implementation from object instance“
• Decide which definition to use based on calling object”
– Very important OOP principle! 15-4
Virtual Functions via Example
• You are designing record-keeping program for automotive parts
store
– You want to track sales, but you don’t know all sales yet
– 1st there is only regular retail sales
– However, later you might want to add Discount sales, mail-order sales, etc.
• Depend on other factors besides just price, tax

• Program must:
– Compute daily gross sales (which is sum of all the individual sales bills)
– Calculate largest/smallest sales of day and perhaps average sale for day
• All of these can be calculated from individual bills
– But many functions for computing bills will be added "later"!
• When different types of sales added!

• To accommodate this, function for "computing a bill" will be virtual!


15-5
Class Sale Definition
//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
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.
#endif // SALE_H

15-6
//This is the file sale.cpp. Implementation for the class Sale.
#include <iostream>

Figures Example: Virtual!


#include "sale.h"
using std::cout;
Sale::Sale( ) : price(0) {}
Sale::Sale( double thePrice) {
if (thePrice >= 0) price = thePrice;
else {
cout << "Error: Cannot have a negative price!\n";
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!\n";
exit(1);
}
} • Notice BOTH use
double Sale::savings( const Sale &other) const { member function
return (bill( ) - other.bill( )); bill()!
} • We can later define
bool operator < ( const Sale& first, const Sale& second) { derived classes of
return (first.bill( ) < second.bill( )); Sale and define their
} versions of bill()
Class Sale and DiscountSale
• Class Sale: represents sales of single item with no added discounts
or charges.
– "virtual“ reserved keyword in declaration of member function bill
• Impact: Later, derived classes of Sale can define THEIR versions of bill
• Other member functions of Sale will use version based on object of derived class!
• They won’t automatically use Sale’s version!

• Class DiscountSale (next slide): derived from Sale


– DiscountSale’s member function bill() implemented differently than
Sale’s
• Particular to "discounts"
– Member functions savings and "<"
• Will use this definition of bill() for all objects of DiscountSale class!
• Instead of "defaulting" to version defined in Sales class!

15-8
Derived Class DiscountSale Defined
//This is the file discountsale.h.
//This is the interface for the class DiscountSale.
#ifndef DISCOUNTSALE_H
#define DISCOUNTSALE_H
#include "sale.h"
class DiscountSale : public Sale {
public:
DiscountSale( );
DiscountSale( double thePrice, double theDiscount);
//Discount is expressed as a percentage of the price.
//A negative discount is a price increase.
double getDiscount( ) const;
void setDiscount( double newDiscount);
double bill( ) const;
private: • Since bill was declared virtual in the base class, it is
double discount; automatically virtual in the derived class DiscountSale.
}; • You can add the modifier virtual to the declaration of
#endif //DISCOUNTSALE_H 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 for readability, even if it is not
required. We omitted it here to illustrate that it is not
required.)
// discountsale.cpp is the implementation for the class DiscountSale.
//The interface for the class DiscountSale is in discountsale.h.
#include "discountsale.h"
discountsale.cpp
DiscountSale::DiscountSale( ) : Sale( ), discount(0) {}
DiscountSale::DiscountSale(double thePrice, double theDiscount)
: Sale(thePrice), discount(theDiscount){}
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( );
}

• Qualifier "virtual" does not go in actual function definition


//Demonstrates the performance of the virtual function bill.
#include <iostream>
#include "sale.h" //Not really needed, but safe due to ifndef. Tester1.cpp
#include "discountsale.h"
using std::cout;
using std::endl;
using std::ios;
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); Discounted item is cheaper.
cout.setf(ios::showpoint); Savings is $0.10
cout.precision(2);
if (discount < simple){
cout << "Discounted item is cheaper.\n";
cout << "Savings is $" << simple.savings(discount) << endl;
}
else
cout << "Discounted item is not cheaper.\n";
return 0;
}
• Recall class Sale written long before derived class DiscountSale
– savings and "<" compiled before even had ideas of DiscountSale class
• Yet in a call like: simple.savings(discount)
– Call in savings()to bill()knows to use definition of bill()from DiscountSale
• Powerful!
Pure Virtual Functions
• Base class might not have meaningful definition for some of its
members!
• Recall class Figure: All figures are objects of derived classes
– Rectangles, circles, triangles, etc.
– Class Figure has no idea how to draw!
• Make it a pure virtual function: virtual void draw() = 0;
– Pure virtual functions require no definition but Forces all derived classes to
define "their own" version
• abstract base class : Class with one or more pure virtual functions
– Can only be used as base class
– No objects can ever be created from it, since it doesn’t have complete "definitions"
of all it’s members!

• If derived class fails to define all pure’s:


– It’s an abstract base class too 15-12
Extended Type Compatibility Example
//Program to illustrate use of a virtual function to defeat the slicing problem.
#include <string>
#include <iostream>
using namespace std;
class Pet {
public:
string name; // For example purposes only! Not typical!
virtual void print( ) const;
};
class Dog : public Pet {
public:
string breed;
virtual void print( ) const;
};
void Dog::print( ) const {
cout << "name: " << name << endl;
cout << "breed: " << breed << endl;
}
void Pet::print( ) const {
cout << "name: " << name << endl; //Note that no breed is mentioned.
}

15-13
int main( ) {
Dog vdog;
Pet vpet;
vdog.name = "Tiny";
Pets.cpp
vdog.breed = "Great Dane";
vpet = vdog; //Derived objects can be assigned to objects of type Base But NOT the other
way!
cout << "The slicing problem:\n";
//vpet.breed; is illegal since class Pet has no member named breed.
vpet.print( );
cout << "Note that it was print from Pet that was invoked.\n";
cout << "The slicing problem defeated:\n";
The slicing problem:
Pet *ppet; name: Tiny
Dog *pdog; Note that it was print from Pet that was invoked.
pdog = new Dog; The slicing problem defeated:
pdog->name = "Tiny"; name: Tiny
pdog->breed = "Great Dane"; breed: Great Dane
ppet = pdog; name: Tiny
ppet->print( ); breed: Great Dane
pdog->print( );
//The following, which accesses member variables directly
//rather than via virtual functions, would produce an error:
//cout << "name: " << ppet->name << " breed: “ << ppet->breed << endl;
//It generates an error message saying class Pet has no member named breed.
return 0;
} 15-14
Using Classes Pet and Dog and slicing problem
vdog.name = "Tiny";
• Anything that "is a" dog "is a" pet: vdog.breed = "Great Dane";
vpet = vdog;
• Can assign values to parent-types, but not reverse
– A dog is a pet but a pet "is not a" dog (not necessarily)
• Slicing problem
– Notice value assigned to vpet "loses" its breed field!
• cout << vpet.breed; //Produces ERROR msg! slicing problem
• Slicing problem fix
– In C++, slicing problem is nuisance
• It still "is a" Great Dane named Tiny
• We’d like to refer to its breed even if it’s been treated as a Pet
– Can do so with pointers to dynamic variables
• Must use virtual member function: ppet->print();
– Calls print member function in Dog class!
– Because print() is virtual, C++ "waits" to see what object pointer ppet
is actually pointing to before "binding" call 15-15
Virtual Destructors
• Recall: destructors needed to de-allocate dynamically allocated
data
• Consider:
Base *pBase = new Derived;

delete pBase;
– Would call base class destructor even though pointing to Derived class
object!
– Making destructor virtual fixes this!
• Good policy for all destructors to be virtual

15-16
Casting
• Consider: Pet vpet;
Dog vdog;

vdog = static_cast<Dog>(vpet); //ILLEGAL!
– Static downcasting is not allowed: Casting from ancestor type to descendant
– Can’t cast a pet to be a dog,
– but upcasting is OK: From descendant type to ancestor type
vpet = vdog; // Legal!
vpet = static_cast<Pet>(vdog); //Also legal!

• Downcasting allowed with dynamic_cast but it is dangerous and


rarely done

Copyright © 2017 Pearson Education, Ltd. All rights reserved. 15-17

You might also like