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

Lecture 16 - Virtual Functions and Polymorphism

Uploaded by

hogikid600
Copyright
© © All Rights Reserved
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

Lecture 16 - Virtual Functions and Polymorphism

Uploaded by

hogikid600
Copyright
© © All Rights Reserved
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 33

Comsat Institute of

Information Technology
wah

Lecture 16
Virtual Functions and
Polymorphism
Umer Farooq
Review — Accessing Members of
Base and Derived Classes
class B { • The following are legal:–
public: B_obj.m() //B’s m()
void m(); B_obj.n()
void n();
... D_obj.m() //D’s m()
} // class B D_obj.n() //B’s n()
D_obj.p()

class D: public B {
B_ptr->m() //B’s m()
public
B_ptr->n()
void m();
void p();
D_ptr->m() //D’s m()
...
D_ptr->n() //B’s n()
} // class D D_ptr->p()
Review — Accessing Members of
Base and Derived Classes (continued)
class B { • The following are legal:–
public: B_ptr = D_ptr;
void m(); • The following are not legal:–
void n(); D_ptr = B_ptr;
... B_ptr->p();
} // class B Even if B_ptr is known to point
to an object of class D
class D: public B {
public
void m();
void p(); Class D redefines method m()
...
} // class D
Review — Accessing Members of
Base and Derived Classes (continued)
• Access to members of a class object is determined by
the type of the handle.

• Definition: Handle
• The thing by which the members of an object are accessed
• May be
– An object name (i.e., variable, etc.)
– A reference to an object
– A pointer to an object
Review — Accessing Members of
Base and Derived Classes (continued)
• This is referred to as static binding

• I.e., the binding between handles and members


is determined at compile time
• I.e., statically
What if we need Dynamic
Binding?
• I.e., what if we need a class in class Rectangle: public Shape {
which access to methods is public:
void Rotate();
determined at run time by the type void Draw();
of the object, not the type of the ...
handle }

class Shape {
class Ellipse: public Shape {
public:
void Rotate(); public:
void Rotate();
void Draw();
d to void Draw();
... th o
me ject ...
} the s s f ob }
e
c c k in d o
a
to ight e!
d d l
Nee the r of han
w
d r a r d l es s
e g a
R
Solution – Virtual Functions

• Define a method as virtual, and the subclass


method overrides the base class method

• E.g.,
class Shape {
This tells the compiler to add
public: internal pointers to every object
virtual void Rotate(); of class Shape and its derived
virtual void Draw(); classes, so that pointers to
... correct methods can be stored
} with each object.
What if we need Dynamic
Binding?
class Shape { class Rectangle: public Shape
{
public:
virtual void Rotate(); public:
virtual void Draw(); void Rotate();
... void Draw();
...
}
}
• I.e., subclass methods
override the base class class Ellipse: public Shape {
methods public:
void Rotate();
– (if specified)
void Draw();
• C++ dynamically chooses the ...
correct method for the class }
from which the object was
instantiated.
Notes on virtual

• If a method is declared virtual in a class,


– … it is automatically virtual in all derived classes

• It is a really, really good idea to make


destructors virtual!
virtual ~Shape();
– Reason: to invoke the correct destructor, no matter
how object is accessed
Virtual Destructors

• Constructors cannot be virtual, but


destructors can be virtual.

• It ensures that the derived class destructor


is called when a base class pointer is used
while deleting a dynamically created
derived class object.
Virtual Destructors (contd.)
class base { int main()
public: {
~base() {
cout << “destructing base\n”;
base *p = new derived;
} delete p;
}; return 0;
}
class derived : public base {
public:
~derived() {
Output:
destructing base
cout << “destructing derived\n”;
}
};

Using non-virtual destructor


Virtual Destructors (contd.)
class base { int main()
{
public:
virtual ~base() {
base *p = new derived;
cout << “destructing base\n”; delete p;
}
}; return 0;
}
class derived : public base {
public:
~derived() { Output:
cout << “destructing derived\n”; destructing derived
} destructing base
};

Using virtual destructor


Notes on virtual (continued)
• A derived class may optionally override a virtual function
• If not, base class method is used

class Shape {
public:
virtual void Rotate();
virtual void Draw();
...
}
class Line: public Shape {
public:
void Rotate();
//Use base class Draw method
...
}
Summary – Based and Derived
Class Pointers
• Base-class pointer pointing to base-class object
– Straightforward
• Derived-class pointer pointing to derived-class object
– Straightforward
• Base-class pointer pointing to derived-class object
– Safe
– Can access non-virtual methods of only base-class
– Can access virtual methods of derived class
• Derived-class pointer pointing to base-class object
– Compilation error
Abstract and Concrete Classes
• Abstract Classes
– Classes from which it is never intended to instantiate any objects
• Incomplete—derived classes must define the “missing pieces”.
• Too generic to define real objects.
Definitions
– Normally used as base classes and called abstract base classes
• Provide appropriate base class frameworks from which other
classes can inherit.

• Concrete Classes
– Classes used to instantiate objects
– Must provide implementation for every member function they
define
Pure virtual Functions

• A class is made abstract by declaring one


or more of its virtual functions to be “pure”
– I.e., by placing "= 0" in its declaration

• Example
virtual void draw() const = 0;

– "= 0" is known as a pure specifier.


– Tells compiler that there is no implementation.
Polymorphism CS-2303, C-Term 2010 16
Pure virtual Functions
(continued)
• Every concrete derived class must override all
base-class pure virtual functions
– with concrete implementations

• If even one pure virtual function is not


overridden, the derived-class will also be
abstract
– Compiler will refuse to create any objects of the class
– Cannot call a constructor
Purpose

• When it does not make sense for base


class to have an implementation of a
function

• Software design requires all concrete


derived classes to implement the function
• Themselves
Why Do we Want to do This?
• To define a common public interface for the
various classes in a class hierarchy
– Create framework for abstractions defined in our
software system

• The heart of object-oriented programming

• Simplifies a lot of big software systems


• Enables code re-use in a major way
• Readable, maintainable, adaptable code
Case Study: Payroll System Using Polymorphism

• Create a payroll program


– Use virtual functions and polymorphism
• Problem statement
– 4 types of employees, paid weekly
• Salaried (fixed salary, no matter the hours)
• Hourly workers
• Commission (paid percentage of sales)
• Base-plus-commission (base salary + percentage of
sales)
Case Study: Payroll System Using Polymorphism

• Base class Employee


– Pure virtual function earnings (returns pay)
• Pure virtual because need to know employee type
• Cannot calculate for generic employee
– Other classes derive from Employee

Employee

SalariedEmployee CommissionEmployee HourlyEmployee

BasePlusCommissionEmployee
Employee Example
class Employee {
public:
Employee(const char *, const char *);
~Employee();
char *getFirstName() const;
char *getLastName() const;

// Pure virtual functions make Employee abstract base


class.
virtual float earnings() const = 0; // pure virtual
virtual void print() const = 0; // pure virtual

protected:
char *firstName;
char *lastName;
};
Employee::Employee(const char *first, const char *last)
{
firstName = new char[ strlen(first) + 1 ];
strcpy(firstName, first);
lastName = new char[ strlen(last) + 1 ];
strcpy(lastName, last);
}

// Destructor deallocates dynamically allocated memory


Employee::~Employee() {
delete [] firstName; delete [] lastName;
}

// Return a pointer to the first name


char *Employee::getFirstName() const {
return firstName; // caller must delete memory
}

char *Employee::getLastName() const {


return lastName; // caller must delete memory
}
class SalariedEmployee: public Employee {
public:
SalariedEmployee(const char *, const char *, float = 0.0);
void setWeeklySalary(float);
virtual float earnings() const;
virtual void print() const;
private:
float weeklySalary;
};
// Constructor function for class
SalariedEmployee:: SalariedEmployee(const char *first,
const char *last, float s)
: Employee(first, last) // call base-class constructor
{ weeklySalary = s > 0 ? s : 0; }

// Set the SalariedEmployee’s salary


void SalariedEmployee::setWeeklySalary(float s)
{ weeklySalary = s > 0 ? s : 0; }

// Get the SalariedEmployee’s pay


float SalariedEmployee::earnings() const { return weeklySalary; }

// Print the SalariedEmployee’s name


void SalariedEmployee::print() const
{
cout << endl << " Salaried Employee: " << getFirstName()
<< ' ' << getLastName();
}
class CommissionWorker : public Employee {
public:
CommissionWorker(const char *, const char *, float = 0.0, unsigned = 0);
void setCommission(float);
void setQuantity(unsigned);
virtual float earnings() const;
virtual void print() const;

private:
float commission; // amount per item sold
unsigned quantity; // total items sold for week
};
CommissionWorker::CommissionWorker(const char *first,
const char *last, float c, unsigned q)
: Employee(first, last) // call base-class constructor
{
commission = c > 0 ? c : 0;
quantity = q > 0 ? q : 0;
}
void CommissionWorker::setCommission(float c)
{ commission = c > 0 ? c : 0; }
void CommissionWorker::setQuantity(unsigned q)
{ quantity = q > 0 ? q : 0; }
float CommissionWorker::earnings() const
{ return commission * quantity; }
void CommissionWorker::print() const
{
cout << endl << "Commission worker: " << getFirstName()
<< ' ' << getLastName();
}
class HourlyWorker : public Employee {
public:
HourlyWorker(const char *, const char *,
float = 0.0, float = 0.0);
void setWage(float);
void setHours(float);
virtual float earnings() const;
virtual void print() const;
private:
float wage; // wage per hour
float hours; // hours worked for week
};
HourlyWorker::HourlyWorker(const char *first, const char *last,
float w, float h)
: Employee(first, last) // call base-class constructor
{
wage = w > 0 ? w : 0;
hours = h >= 0 && h < 168 ? h : 0;
}
void HourlyWorker::setWage(float w) { wage = w > 0 ? w : 0; }
// Set the hours worked
void HourlyWorker::setHours(float h)
{ hours = h >= 0 && h < 168 ? h : 0; }
// Get the HourlyWorker's pay
float HourlyWorker::earnings() const { return wage * hours; }
// Print the HourlyWorker's name
void HourlyWorker::print() const
{
cout << endl << " Hourly worker: " << getFirstName()
<< ' ' << getLastName();
}
class BasePlusCommissionEmployee:public
CommissionWorker
{
private:
float baseSalary;
public:
BasePlusCommissionEmployee(const char* ,
const char* , float =0.0, unsigned =0,float =0.0);
void setBaseSalary(float sal) {
baseSalary = sal;
}
float getBaseSalary(void) const {
return baseSalary;
}
void print() const;
float earnings() const;
};
BasePlusCommissionEmployee::BasePlusCommissionEmployee(co
nst char* first, const char* last, float c,
unsigned q,float sal)
:CommissionWorker(first,last,c,q)
{
baseSalary=(sal);
}
void BasePlusCommissionEmployee::print() const
{
cout << "\nbase-salaried commission employee: ";
CommissionWorker::print(); // code reuse
} // end function print
float BasePlusCommissionEmployee::earnings() const
{
return getBaseSalary() + CommissionWorker::earnings();

} // end function earnings


void main(void)
{
Employee *ptr; // base-class pointer

SalariedEmployee b(“Nauman", "Sarwar", 800.00);


ptr = &b; // base-class pointer to derived-class object
ptr->print(); // dynamic binding
cout << " earned $" << ptr->earnings(); // dynamic binding
b.print(); // static binding
cout << " earned $" << b.earnings(); // static binding

CommissionWorker c(“Qasim", “Ali", 3.0, 150);


ptr = &c; // base-class pointer to derived-class object
ptr->print(); // dynamic binding
cout << " earned $" << ptr->earnings(); // dynamic binding
c.print(); // static binding
cout << " earned $" << c.earnings(); // static binding
BasePlusCommissionEmployee p("Mehshan", "Mustafa", 2.5, 200, 1000.0);
ptr = &p; // base-class pointer to derived-class object
ptr->print(); // dynamic binding
cout << " earned $" << ptr->earnings(); // dynamic binding
p.print(); // static binding
cout << " earned $" << p.earnings(); // static binding

HourlyWorker h(“Samer", “Tufail", 13.75, 40);


ptr = &h; // base-class pointer to derived-class object
ptr->print(); // dynamic binding
cout << " earned $" << ptr->earnings(); // dynamic binding
h.print(); // static binding
cout << " earned $" << h.earnings(); // static binding

cout << endl;

return 0;
}

You might also like