You are on page 1of 30

1

COEN 244 – PROGRAMMING METHODOLOGY II

Topic 06
Polymorphism, Dynamic
Binding, Abstract Classes
2

What is Polymorphism?
• The Greek meaning of the words "poly" and
"morph" together imply that "a single entity can
take on multiple forms"

• In object-oriented programming, polymorphism


consists of the the ability to have the same
operation implemented in various ways using the
same function name
3

Types of Polymorphisms in C++?


• C++ supports three types of polymorphisms
– Ad-hoc polymorphism: applies to function overloading
– Subtype polymorphism: applies to function overriding It
is also called dynamic polymorphism
– Parametric polymorphism: applies to templates. We will
see that in the next classes
4

Calling Base Class Functions from


Derived Class Objects
• Reminder:
– The derived class object is an object of the base class
– This means all the members of the base class are also
members of the derived class

• We will see
– How a base-class pointer can be used to point to a
derived class object
– How such a pointer can be used to invoke the base
class member functions that are in fact members of the
derived class
5

Example: Class Shape


// shape.h : specification of Shape

class Shape {
public:
Shape (int = 0, int = 0 ); // default constructor
void setX(int); // set x in coordinate pair
int getX() const; // return x from coordinate pair

void setY( int ); // set y in coordinate pair


int getY() const; // return y from coordinate pair

void print() const; // output Shape object

private:
int x; // x part of coordinate pair
int y; // y part of coordinate pair
};
6
// shape.cpp – implementation of Shape

#include <iostream>
using std::cout;
#include “shape.h" // Shape class definition

// constructor
Shape::Shape( int xValue, int yValue) {
x = xValue;
y = yValue ;
}

// set x in coordinate pair


void Shape::setX( int xValue ) {
x = xValue;
}

// return x from coordinate pair


int Shape::getX() const {
return x;
}
7

// set y in coordinate pair


Void Shape::setY( int yValue ) {
y = yValue;
}

// return y from coordinate pair


int Shape::getY() const {
return y;
}

// output Shape object


void Shape::print() const {
cout << '[' << getX() << ", " << getY() << ']';
}
8

Class Circle
// circle.h
#include “shape.h" // Shape class definition

class Circle : public Shape {


public:
// default constructor
Circle( int = 0, int = 0, double = 0.0 );
void setRadius( double ); // set radius
double getRadius() const; // return radius
double getDiameter() const; // return diameter
double getCircumference() const; // return circumference
double getArea() const; // return area
void print() const; // output Circle object

private:
double radius;
};
9

// circle.cpp
#include <iostream>
using std::cout;
#include "circle.h" // Circle class definition

// Constructor
Circle::Circle( int xValue, int yValue, double radiusValue ) : Shape( xValue, yValue )
{
radius = radiusValue;
}

// set radius
void Circle::setRadius( double radiusValue )
{
radius = radiusValue;
}

// return radius
double Circle::getRadius() const
{
return radius;
}
10

// calculate and return diameter


double Circle::getDiameter() const
{
return 2 * getRadius();
}

// calculate and return circumference


double Circle::getCircumference() const
{
return 3.14159 * getDiameter();
}
// calculate and return area
double Circle::getArea() const
{
return 3.14159 * getRadius() * getRadius();
}

// output Circle object


void Circle::print() const
{
cout << "center = ";
Shape::print(); // invoke Shape's print function
cout << "; radius = " << getRadius();
}
11

Test the Classes Shape and Circle


// test.cpp - The driver to test Shape and Circle
#include <iostream>
using namespace std;
#include “shape.h"
#include "circle.h“

int main()
{
Shape* shape = NULL; // You can also use 0
Circle* circle = NULL; // derived-class pointer

shape = new Shape(10.0, 20.5);


shape->print(); // invokes Shape's print

circle = new Circle(30.0, 10.5, 12.5);


circle->print(); // invokes Circle's print

shape = circle;
shape->print(); // Which one is invoked?

return 0;
}
12

Calling Derived-Class Member Functions


Using Base-Class Pointers
• Remember: A base class pointer that points to a
derived class object can be used to invoke the base
class member functions in the derived class. This is
known as down casting.

• However: A base class pointer that points to a derived


class object can not be used to invoke the derived
class member functions that are not members of the
base class, unless explicit cast is made (we will see
this later)
shape = circle; // ok
double radius = shape->getRadius(); //
Error
shape->setRadius( 33.33 ); // Error
13

Static Binding
• Static binding means that the pointer can only
invoke the member functions of the class used
in its definition
– Although the same pointer can point to an object of
another class (a derived class)
14

Dynamic Binding
• Dynamic binding occurs when the decision regarding
which member function to execute is delayed till run-
time
– Depending on the object on which the member
function is invoked

• In C++, dynamic binding is implemented using the


concept of virtual functions

• Syntax of a virtual function is:


virtual return_type function_name (parameters)
Class Shape
// shape.h : specification of Shape

class Shape {
public:
Shape (int = 0, int = 0 ); // default constructor
void setX(int); // set x in coordinate pair
int getX() const; // return x from coordinate pair

void setY( int ); // set y in coordinate pair


int getY() const; // return y from coordinate pair

virtual void print() const; // output Shape object

private:
int x; // x part of coordinate pair
int y; // y part of coordinate pair
};
16

Class Circle
// circle.h
#include “shape.h" // Shape class definition

class Circle : public Shape {


public:
// default constructor
Circle( int = 0, int = 0, double = 0.0 );
void setRadius( double ); // set radius
double getRadius() const; // return radius
double getDiameter() const; // return diameter
double getCircumference() const; // return circumference
double getArea() const; // return area
virtual void print() const; // output Circle object

private:
double radius;
};
17

Testing the classes


// test.cpp - The driver to test Shape and Circle
#include <iostream>
using namespace std;
#include “shape.h"
#include "circle.h“

int main()
{
Shape* shape = NULL; // You can also use 0
Circle* circle = NULL; // derived-class pointer

shape = new Shape(10.0, 20.5);


shape->print(); // invokes Shape's print

circle = new Circle(30.0, 10.5, 12.5);


circle->print(); // invokes Circle's print

shape = circle;
shape->print(); // print of Circle will be invoked?

return 0;
};
18

Why Are Virtual Functions Useful?


• They can be very useful
– Suppose Circle, Triangle, Rectangle derive from
Shape
• Each has its own draw function
– To draw any shape
• Have base class Shape pointer, call draw
• Program determines the proper draw function
at run-time (dynamically)
• Treat all shapes generically
19

Example
• Consider this member function

class A {

void refresh(Shape *);
….
};
void A::refresh(Shape *s) {
s->draw();
}

• Consider the following code. We only only had to define refresh once using
Shape and pass to it an object Rectangle, and then an object Circle.

Rectangle *r = new Rectangle(…);


Circle *c = new Circle(….);
A *obj = new A(…);
obj.refresh(r); // causes draw of rectangle to be called
obj.refress(c); // causes draw of circle to be called
20

Important Rule
• When a member function is declared virtual, it
is virtual in all derived classes

• It is good practice to explicitly declare virtual


even in the derived classes
21

Virtual Destructors
• Consider a derived class object that is dynamically
allocated to a base class pointer
– Deleting such an object by applying “delete” operator to the
base class pointer will only invoke the destructor of the base
class

• The solution for this problem is to use a virtual


destructor at the base class level
– This makes all the destructors down the inheritance
hierarchy virtual

• When applying “delete” operator to the base class


pointer the right virtual destructor is invoked
22

Abstract Classes
• An abstract class, also called abstract base
class, is a class that is meant to be a base
class for other classes for design quality
raisons
– We cannot instantiate them

• For example, an abstract base class Person


can be defined to serve as a base class from
which classes like Employee and Student can
be derived

• The classes derived from an abstract class


are called concrete classes.
23

Abstract Classes (cont.)


• A class becomes an abstract class if it contains one
or more abstract member functions
– Also called pure virtual functions

• Pure virtual functions do not have implementation.


Thus, pure virtual functions must be overridden
by the derived concrete classes

• Pure virtual functions are declared using “=0” after


the function signature. For example:

virtual void print() const = 0; // pure virtual


function
24

Example: Class Employee


From Topic 04 – Inheritance (Case Study)
// employee.h: Specification of the base class Employee
class Employee {

protected:
int id; // employee id
char* name; // name of the employee
char* address; // address of the employee
char* phone; // telephone number, e.g., 514-234-5454
char* email; // email address
int vacation; // days of vacation

public:
Employee(); // default constructor
Employee (int, const char* ); // regular constructor, used to set id and name
virtual ˜Employee(); // virtual destructor

void setName(char*); // modifies the name


char* getName() const; // returns the name
25

void setId(int); // modifies the id


int getId() const; // returns the id
void setAddress(char*); // modifies the address
char* getAddress() const; // returns the address
void setPhone(char*); // modifies the phone number
char* getPhone() const; // returns the phone number
void setEmail(char*); // modifies the email
char* getEmail() const; // returns the email
void setVacation(int); // modifies the number of vacation days
int getVacation() const; // returns the number of vacation days

virtual void print() const; // prints info about an employee


virtual double getPay() const = 0; // pure virtual function – must be
// overridden
};
26

Implementation of the Virtual


Functions of Employee
// Same as before
virtual Employee::~Employee() {
delete [] name; delete [] address;
delete [] phone; delete [] email;
}
// Same as before
virtual void Employee::print() const {
cout << id << “ “ << name << endl;
}

// pure virtual function has no implementation


virtual double Employee::getPay() const = 0;
27

The Class Manager


// manager.h: Specification of class Manager

class Manager : public Employee


{
public:
Manager(); // default constructor
Manager(int, const char*, double); // regular constructor: id, name, salary
virtual ~Manager(); // virtual destructor

void setSalary(double); // modifies salary


void setNumSupervisedEmp(int); // modifies number of supervised employees
int getNumSupervisedEmp() const; // returns number of supervised employees

virtual double getPay() const; // returns pay – provides implementation to getPay


// of the base class

virtual void print() const; // prints manager information - – overrides print of


// Employee
private:
double salary;
int numSupervisedEmp;
};
28
Implementation of the Virtual
Functions of Manager
// Same as before
virtual Manager::~Manager() {
// does nothing
}

// Same as before
virtual void Manager::print() const {
Employee::print();
cout << salary << endl;
}

// Same as before. It provides an implementation to the abstract member


// function getPay of the base class
virtual double Manager::getPay() const {
return salary;
}
29

Example of an
Inheritance
Hierarchy
30

Discussion
• We will discuss the following questions using
the board
– What classes should be abstract classes?
– What member functions should be abstract, i.e.,
pure virtual?
– What member functions should be virtual?

You might also like