Professional Documents
Culture Documents
Lecture9 Polymorphism
Lecture9 Polymorphism
1
Agenda
• What is Polymorphism
• Why polymorphism
• Static and late binding
• Virtual functions
• Type of polymorphism
• Pure virtual functions
• Abstract class
• Virtual inheritance
2
Object-Oriented Concepts
• Data Abstarction
• Encapsulation
• ADT, Object
• Inheritance
• Derived object
• Polymorphism
• Each object knows what it is
3
Polymorphism
• noun, the quality or state of being able to assume
different forms - Webster
• Virtual means existing in appearance but not in reality
• Polymorphism means “many shapes.” It refers to the
ability of one object to have many types. If we have a
function that expects a Vehicle object, we can safely
pass it a Car object, because every Car is also a Vehicle.
Likewise for references and pointers: anywhere you can
use a Vehicle *, you can use a Car *.
• The sending instance does not need to know the
receiving instance’s class and this class can be any class.
4
Before we proceed….
• Inheritance – Basic Concepts
• Class Hierarchy
• Code Reuse, Easy to maintain
• Type of inheritance : public, private, protected
• Function overriding
• Overloading
• Function overloading
• Operator overloading
5
Array of Objects of Different Shapes
• For example, suppose a graphics program includes several different shapes: a
triangle, a ball, a square, and so on..
• Each of these classes has a member function draw() that causes the object to be
drawn on the screen.
• Now suppose the plan is to make a picture by grouping a number of these
elements together, and so that we can draw the picture in a convenient way.
• One approach is to create an array that holds pointers to all the different objects in
the picture
• shape* ptrarr[100]; // array of 100 pointers to shapes
• If you insert pointers to all the shapes into this array, you can then draw an entire
picture using a simple loop:
• for(int j=0; j<N; j++)
• ptrarr[j]->draw();
• Amaging! Each type of shape can have its own draw function that can
be invoked by its own object.
6
7
8
9
Hypothetical program!
10
Non virtual functions invoked
through pointer
11
Class Interface Diagram
ExtTime class Time class
Set Set
12
class Time Specification
// SPECIFICATION FILE ( time.h)
class Time{
public :
void Set ( int h, int m, int s ) ;
void Increment ( ) ;
void Write ( ) const ;
Time ( int initH, int initM, int initS ) ; // constructor
Time (); // default constructor
protected :
int hrs ;
int mins ;
int secs ;
};
13
Class Interface Diagram
Time class
Set
Protected data:
Increment
hrs
Write
mins
Time secs
Time
14
Derived Class ExtTime
// SPECIFICATION FILE ( exttime.h)
#include “time.h”
enum ZoneType {EST, CST, MST, PST, EDT, CDT, MDT, PDT } ;
Set Set
16
Implementation of ExtTime
Default Constructor
ExtTime :: ExtTime (int initH, int initM, int initS, ZoneType initZone)
: Time (initH, initM, initS)
// constructor initializer
{
zone = initZone ;
}
ExtTime *et2 =
5000
new ExtTime(8,30,0,EST);
hrs = 8
et2 6000 mins = 30
5000
??? secs = 0
zone = EST 18
Implementation of ExtTime
void ExtTime :: Set (int h, int m, int s, ZoneType timeZone)
{
Time :: Set (hours, minutes, seconds); // same name function call
zone = timeZone ;
}
Time :: Write ( ) ;
cout <<‘ ‘<<zoneString[zone]<<endl;
}
19
Working with ExtTime
#include “exttime.h”
……
int main()
{
ExtTime thisTime ( 8, 35, 0, PST ) ;
ExtTime thatTime ; // default constructor called
thatTime.Write( ) ; // outputs 00:00:00 EST
thatTime.Set (16, 49, 23, CDT) ;
thatTime.Write( ) ; // outputs 16:49:23 CDT
thisTime.Increment ( ) ;
thisTime.Increment ( ) ;
thisTime.Write ( ) ; // outputs 08:35:02 PST
}
20
Why Polymorphism?--Review:
Time and ExtTime Example by Inheritance
void Print (Time someTime ) //pass an object by value
{
cout << “Time is “ ;
someTime.Write ( ) ;
cout << endl ;
} // Time :: write()
CLIENT CODE
Time startTime ( 8, 30, 0 ) ; OUTPUT
ExtTime endTime (10, 45, 0, CST) ;
Time is 08:30:00
Print ( startTime ) ; Time is 10:45:00
Print ( endTime ) ;
21
Static Binding
• When the type of a formal parameter is a parent class, the argument
used can be:
22
Can we do better?
void Print (Time someTime ) //pass an object by value
{
cout << “Time is “ ;
someTime.Write ( ) ;
cout << endl ;
} // Time :: write()
CLIENT CODE
Time startTime ( 8, 30, 0 ) ; OUTPUT
ExtTime endTime (10, 45, 0, CST) ;
Time is 08:30:00
Print ( startTime ) ; Time is 10:45:00
Print ( endTime ) ;
23
Virtual Member Function Example
• When virtual functions are used, a program that appears to be calling a function
of one class may in reality be calling a function of a different class.
24
This is the way we like to see…
void Print (Time * someTime )
{
cout << “Time is “ ;
someTime->Write ( ) ;
cout << endl ;
} OUTPUT
Time *timeptr;
timeptr = &startTime;
Print ( timeptr ) ; Time::write()
timeptr = &endTime;
Print ( timeptr ) ; ExtTime::write()
25
Dynamic Binding
• Is the run-time determination of which function to call for a
particular object of a derived class based on the type of the
argument
26
Virtual Functions (I)
• Virtual Functions overcome the problem of run time object determination
• Keyword virtual instructs the compiler to use late binding and delay the
object interpretation
• How ?
• Define a virtual function in the base class. The word virtual appears only in the
base class
• If a base class declares a virtual function, it must implement that function, even
if the body is empty
• The Virtual Attribute Is Inherited
• This means that when a derived class that has inherited a virtual function is itself
used as a base class for another derived class, the virtual function can still be
overridden.
• Virtual function in base class stays virtual in all the derived classes
• But, a derived class is not required to re-implement a virtual function. If it does
not, the base class version is used
27
Virtual Functions (II)
• Member function preceded by keyword ‘virtual’ in base
class and overridden in derived class
• Always needs to be called with base class pointer or
reference
• When base class pointer points at derived class object,
c++ determines which copy to be called depending upon
the type of the object at run time
• If object of base class invokes virtual function, then base
class implementation is invoked and if derived class
object invokes it, then derived class implementation is
invoked.
• They are resolved at run time not at compile time
Virtual Functions (III)
• General rules while defining virtual function:
1. Must be member of some class
2. Accessed using object pointers
3. Can be friend of another class
4. Signature of base class and derived class virtual
function must be identical
5. No need to use keyword ‘virtual’ in definition if its is
defined outside the class
6. Can not be a static member
7. Can not be a constructor
• Because when constructor of a class is executed there is no vtable
in the memory, means no virtual pointer defined yet.
Polymorphism Example
Virtual functions invoked through
pointer
31
Late Binding
ptr->show();
• It always compiles a call to the show() function in the base class.
• But the compiler doesn’t know what class the contents of ptr may contain.
It could be the address of an object of the Derv1 class or of the Derv2 class.
• Which version of show() does the compiler call? In fact the compiler
doesn’t know what to do, so it arranges for the decision to be deferred until
the program is running.
• At runtime, when it is known what class is pointed to by ptr, the appropriate
version of show() will be called. This is called late binding or dynamic
binding. Uses a technique called virtual table.
• Late binding requires some overhead but provides increased power and
flexibility. Because it searches the virtual table to identify right code to be
executed.
• We’ll put these ideas to use in a moment, but first let’s consider a
refinement to the idea of virtual functions.
32
Static and Dynamic binding defined
• A service can have different implementations
• Different objects
• Provides different behaviors
• Client can uniformly issue requests for the service
• The selection of code to perform a service is called
binding
• Dynamic binding -the object is identified when the
request is actually issued
• Static binding – code selection is done during code
compilation or linking
Example
Vehicle
getDesc()
Car Bus
34
Virtual Functions: Revisit
Car c(" VANITY " ,2003) ;
Vehicle * vPtr = & c;
cout<< vPtr -> getDesc () ;
1 class Vehicle {
2 ...
3 virtual const string get Desc () {...}
4 };
35
Virtual Functions: Revisit
• With this definition, the code above would correctly select the Car
version of getDesc.
• Selecting the correct function at runtime is called dynamic dispatch. This
matches the whole OOP idea – we’re sending a message to the object
and letting it figure out for itself what actions that message actually
means it should take.
• Because references are implicitly using pointers, the same issues apply
to references.
36
Pure virtual Functions
• Arguably, there is no reasonable way to define getDesc for a generic
Vehicle.
• only derived classes really need a definition of it.
• since there is no such thing as a generic vehicle that isn’t also a car,
truck, or the like.
• Still, we do want to require every derived class of Vehicle to have this
function. How do we do??
• Make virtual function as a pure virtual function.
class Vehicle {
...
virtual const string get Desc () = 0; // Pure virtual
};
• The = 0 indicates that no definition will be given. Only declared.
• It implies that one can no longer create an instance of Vehicle; one can
only create instances of Cars, Trucks, and other derived classes which do
implement the getDesc method.
• Vehicle is then an abstract class
• one which defines only an interface, but doesn’t actually implement it, and 37
therefore cannot be instantiated.
Pure virtual Functions
• It is virtual member function of base class without definition.
Should be overridden in all the derived classes
• Pure virtual function is one with the expression =0 added to the
declaration.
• Is initialized to 0. “=0” indicates that code for the function is
null pointer.
• Syntax
virtual type func-name(parameter-list) = 0;
• The equal sign here has nothing to do with assignment; the value
0 is not assigned to anything.
• The =0 syntax is simply how we tell the compiler that a virtual
function is pure.
38
Pure VirtualFunction
Example
class Shape
{
virtual void area() = 0;
};
• If derived class fails to provide definition for the
function, then it becomes an abstract class and
instance of it can not be created then.
Example: Shape yet again
Shape
Circle Rectangle
40
Abstract Classes
• Some classes exist logically but not physically.
• Example : Shape
• Shape s; // Legal but silly..!! : “Shapeless shape”
• Shape makes sense only as a base of some classes derived from it. Serves as a
“category”
• Hence instantiation of such a class must be prevented
Rectangle r; // Valid
Circle c; // error : variable of an abstract class
42
Pure virtual functions : Summary
• Pure virtual functions are useful because they make explicit the
abstractness of a class
• Tell both the user and the compiler how it was intended to be used
• Note : It is a good idea to keep the common code as close as possible to
the root of your hierarchy
43
Summary ..continued
• It is still possible to provide definition of a pure virtual function in the
base class
• The class still remains abstract and functions must be redefined in
the derived classes, but a common piece of code can be kept there to
facilitate reuse
• In this case, they can not be declared inline
47
class Base
{
public:
~Base()
{
std::cout << "Destroying base" << std::endl;
} int main()
}; {
Base* p = new Derived(5);
class Derived : public Base
{
delete p;
public: }
Derived(int number) This will output:
{ > Destroying base
some_resource_ = new int(number);
}
Making Base’s destructor virtual will
~Derived() result in the expected behaviour:
{
std::cout << "Destroying derived" << std::endl;
delete some_resource_;
> Destroying derived
} > Destroying base
private:
int* some_resource_;
};
48
Revisit: Hybrid Inheritance
49
Example: Hybrid Inheritance
Parent
basedata
Child1 Child2
GrandChild
getdata()
50
Example code: Hybrid Inheritance
// ambiguous reference to base class
class Parent
{
protected:
int basedata;
};
class Child1 : public Parent
{ };
class Child2 : public Parent
{ };
class Grandchild : public Child1, public Child2
{
public:
int getdata()
{ return basedata; } // ERROR: ambiguous
};
51
Ambiguity!
• Consider the situation shown in Figure, with a base class, Parent; two
derived classes, Child1 and Child2; and a fourth class, GrandChild,
derived from both Child1 and Child2.
• In this arrangement a problem can arise if a member function in the
Grandchild class wants to access data or functions in the Parent class.
• A compiler error occurs when the getdata() member function in
Grandchild attempts to access basedata in Parent.
• Why? When the Child1 and Child2 classes are derived from Parent,
each inherits a copy of Parent; this copy is called a subobject. Each of
the two subobjects contains its own copy of Parent’s data, including
basedata.
• Now, when Grandchild refers to basedata, which of the two copies
will it access? The situation is ambiguous, and that’s what the
compiler reports.
52
Virtual base classes
• Virtual base classes are relate to multiple inheritance.
• To eliminate the ambiguity, we make Child1 and Child2 into virtual base classes
54
Accessing the class size
#include <iostream>
using namespace std;
class NonVClass {
int x=10;
public:
void foo() {}
};
class VClass {
int y=20;
float z=30.5;
public:
void foo() {}
};
int main() {
cout << "Size of NonVClass: " << sizeof(NonVClass) << endl;
cout << "Size of VClass: " << sizeof(VClass) << endl;
}
55
Accessing the class size
#include <iostream>
using namespace std;
class NonVClass {
public:
void foo() {}
};
class VClass {
public:
void foo() {}
};
int main() {
cout << "Size of NonVClass: " << sizeof(NonVClass) << endl;
cout << "Size of VClass: " << sizeof(VClass) << endl;
}
56
Accessing the class size
#include <iostream>
using namespace std;
class NonVirtualClass {
public:
void foo() {}
};
class VirtualClass {
public:
virtual void foo() {}
};
int main() {
cout << "Size of NonVirtualClass: " << sizeof(NonVirtualClass) << endl;
cout << "Size of VirtualClass: " << sizeof(VirtualClass) << endl;
}
57
Accessing the class size
#include <iostream>
using namespace std;
class NonVirtualClass {
public: Output:
void foo() {} Size of NonVirtualClass: 1
}; Size of VirtualClass: 8
class VirtualClass {
public:
virtual void foo() {}
};
int main() {
cout << "Size of NonVirtualClass: " << sizeof(NonVirtualClass) << endl;
cout << "Size of VirtualClass: " << sizeof(VirtualClass) << endl;
}
• NonVirtualClass has a size of 1 because in C++ classes can’t have zero size.
• VirtualClass’s size is 8 on a 64 bit machine. Why? Because there’s a hidden
pointer inside it pointing to a vtable.
58
Virtual Tables
• Virtual functions in C++, are implemented using the concept
of virtual tables (vtable) to achieve polymorphism.
• Vtable:
• Whenever a class itself contains virtual functions or overrides virtual
functions from a parent class the compiler builds a vtable for that
class.
• Not all classes have a vtable created for them by the compiler.
• The vtable contains function pointers that point to the virtual
functions in that class.
• There can only be one vtable per class
• All objects of the same class will share the same vtable
• The layout is generally compiler-specific and (somewhat) stable
59
int main()
class base { { base* p;
public: derived obj1;
void fun_1() { cout << "base-1\n"; } p = &obj1;
virtual void fun_2() { cout << "base-2\n"; } // Early binding because fun1() is non-virtual
virtual void fun_3() { cout << "base-3\n"; } // in base
virtual void fun_4() { cout << "base-4\n"; } p->fun_1();
}; // Late binding
class derived : public base { p->fun_2();
public: // Late binding
void fun_1() { cout << "derived-1\n"; } p->fun_3();
void fun_2() { cout << "derived-2\n"; } // Late binding
void fun_4(int x) { cout << "derived-4\n"; } p->fun_4();
}; // Early binding but this function call is
//illegal(produces error) because pointer is of
//base type and function is of derived class
// p->fun_4(5);
}
60
Output:
base-1
derived-2
base-3
base-4
61
int main()
class base { { base* p;
public: derived obj1;
void fun_1() { cout << "base-1\n"; } p = &obj1;
virtual void fun_2() { cout << "base-2\n"; } // Early binding because fun1() is non-virtual
virtual void fun_3() { cout << "base-3\n"; } // in base
virtual void fun_4() { cout << "base-4\n"; } p->fun_1();
}; // Late binding
class derived : public base { p->fun_2();
public: // Late binding
void fun_1() { cout << "derived-1\n"; } p->fun_3();
void fun_2() { cout << "derived-2\n"; } // Late binding
void fun_4(int x) { cout << "derived-4\n"; } p->fun_4();
}; // Early binding but this function call is
//illegal(produces error) becasue pointer is of
//base type and function is of derived class
p->fun_4(5);
}
62
Example
class B
{
public:
virtual void bar();
virtual void quick();
};
void B::bar()
{ std::cout << "This is B's implementation of bar" << std::endl; }
void B::quick()
{ std::cout << "This is B's implementation of quck" << std::endl; }
class C : public B
{
public:
void bar() override;
};
void C::bar()
{
std::cout << "This is C's implementation of bar" << std::endl;
}
63
Challenge
• Now consider the following call to bar():
B* b = new C();
b->bar();
64
Example: Dynamic Dispatch - Virtual Tables
• The vtable contains an entry for each virtual function accessible by the
class and stores a pointer to its definition.
• Entries in the vtable can point to either functions declared in the class
itself (e.g. C::bar()), or virtual functions inherited from a base class (e.g.
C::qux()).
65
• The vtable of class B has two entries, one for each of the two
virtual functions declared in B’s scope: bar() and qux().
Additionally, the vtable of B points to the local definition of
functions, since they are the most specific (and only) from
B’s point of view.
• More interesting is C’s vtable. In this case, the entry for bar()
points to own C’s implementation, given that it is more
specific than B::bar(). Since C doesn’t override qux(), its
entry in the vtable points to B’s definition (the most specific
definition).
• Note that vtables exist at the class level, meaning there
exists a single vtable per class, and is shared by all instances.
66
• You might be thinking: vtables are cool and all, but how
exactly do they solve the problem of dynamic dispatch?
• When the compiler sees b->bar() in the example above, it
will lookup B’s vtable for bar’s entry and follow the
corresponding function pointer, right? We would still be
calling B::bar() and not C::bar()…
67
• Note that the vpointer is just another class member added by the
compiler and increases the size of every object that has a vtable by
sizeof(vpointer)
• when a call to a virtual function on an object is performed, the
vpointer of the object is used to find the corresponding vtable of the
class.
• Next, the function name is used as index to the vtable to find the
correct (most specific) routine to be executed.
68
• Hopefully you have grasped how dynamic function dispatch can be
implemented by using vtables:
1. when a call to a virtual function on an object is performed, the vpointer of
the object is used to find the corresponding vtable of the class.
2. Next, the function name is used as index to the vtable to find the correct
(most specific) routine to be executed. Done!
69
class shape { Obj_Shape
public:
shape(); // constructor position
virtual double area() const;
virtual double perimeter() const;
outline
~~~
private:
coordinates position; fill
color outline, fill;
};
70
Code segment
position Shape::perimeter
outline
fill
71
class circle: public shape {
public:
circle(double r); // constructor circle circle vtpl
virtual double area() const;
vptr circle::area
virtual double perimeter() const;
~~~
position circle::perimeter
private:
double radius; outline
};
fill Base class (Shape) subobject
radius
72
class rectangle: public shape { rectangle
rectangle vtpl
public:
rectangle(double h, double w); vptr rectangle::area
virtual double area() const;
position rectangle::perimeter
virtual double perimeter() const;
~~~
outline
private:
double height, width; Base class (Shape) subobject
fill
};
height
width
Obj 1
vptr
position
Code segment
outline Shape vtpl
fill
Pointer to Shape::area
Obj 2 area() code
vptr
position Pointer to Shape::perimeter
perimeter() code
outline
fill
Obj n
vptr
position
outline
fill 74
This pointer
• Every object in C++ has access to its own address through an
important pointer called this pointer.
• i.e. this is a keyword that refers to the current instance of the class
• this pointer is an implicit parameter to all member functions.
• Therefore, inside a member function, this may be used to refer to the
invoking object. (Can't be used in static member functions! Why?)
• Only member functions have a this pointer.
• Friend functions do not have a this pointer, because friends are not
members of a class.
• Uses
• It can be used to pass current object as a parameter to another method.
• It can be used to refer current class instance variable.
• It can be used to declare indexers.
75
class Employee {
public:
int main(void) {
int id; //data member (also instance variable)
Employee e1 =Employee(101, “Somu",
string name; //data member(also instance variable)
890000); //creating an object of Employee
float salary;
Employee e2=Employee(102, "Nakul",
Employee(int id, string name, float salary) 59000); //creating an object of Employee
{ e1.display();
this->id = id; e2.display();
this->name = name; return 0;
this->salary = salary; }
}
void display()
{
cout<<id<<" "<<name<<" "<<salary<<endl;
}
};
76
class Box {
private: int main(void) {
Box Box1(3.3, 1.2, 1.5); // Declare box1
double length; // Length of a box
Box Box2(8.5, 6.0, 2.0); // Declare box2
double breadth; // Breadth of a box
double height; // Height of a box if(Box1.compare(Box2)) {
cout << "Box2 is smaller than Box1"
public:
<<endl;
// Constructor definition } else {
Box(double l = 2.0, double b = 2.0, double h = 2.0) { cout << "Box2 is equal to or larger than
Box1" <<endl;
cout <<"Constructor called." << endl;
}
length = l; breadth = b; height = h;
} return 0;
double Volume() { }
79
80
81
82
Interface
• All functions are pure virtual functions
• No instantiation only for interface referencing
83
Inheritance Vs Composition
• Object composition should be favored than
inheritance
• Composition to flexible doesn’t break encapsulation
• Using Composition it is easy to change objects than super
class code
• Composition promotes smaller, more focused classes and
smaller inheritance hierarchies
• Focus is one task to one class
• Use inheritance to support type, dynamic binding
and polymorphism
Abstract classes Vs Interfaces
• Both mechanisms define required behavior that another class must
implement
• Interfaces allow interface inheritance
• Classes allow implementation inheritance
• Interfaces are limited to public methods and constants with no
implementation allowed
• Classes can have static methods, protected parts and partial
implementation
Abstract classes Vs Interfaces
• Abstract class is easier to evolve over time
• Adding a method that is partial implementation so that the
subclasses do not need to provide their own implementation
unless they want to override the default version
• Adding the functionality will invalidate all the classes that use this
interface
• Classes can allow mutable data field but not interfaces
Abstract classes Vs Interfaces
abstract class time {
public: int getMinutes() = 0;
}
Class days: public time{
private: int days;
public: int getMinutes() { returns days*24*60;
}}
Class hoursMinutes: publictime {
private: int hours;
int minutes;
public: int getMinutes(){ return hours*60+minutes;}}
Abstract classes Vs Interfaces
Interface time{
int getMinutes()=0;}
Class days: public time{
private: int days;
public: int getMinutes(){ returns days*24*60;
}}
Class hoursMinutes: publictime{
Private: int hours;
int minutes;
Public: int getMinutes() {
Returns hours*60+minutes;
}}
How to implement getSeconds()
• Polymorphic
• Values, functions and variables are of more than one type
• Varieties of polymorphism
• Universal polymorphism
• Parametric polymorphism
• Inclusion polymorphism
• Ad hoc polymorphism
• Overloading
• coercion
Polymorphism contd..
• Parametric polymorphism - function works
uniformly on a range of types
• the function has explicit or implicit type parameter which
determines the type of the argument for each application of that
function
• Inclusion polymorphism - object is viewed as
belonging to many different classes that need not
be disjoint.
• Subtyping is an instance of inclusion polymorphism
• Maruthi is a vehicle
Polymorphism contd..
96
Conclusions
• Polymorphism is built upon class inheritance
97