POLYMORPHISM
Polymorphism
Compile-time binding ( Static binding )
Compile-time binding is to associate a function’s
name with the entry point of the function at
compile time.
Example:
#include <iostream>
using namespace std;
void sayHi();
int main(){
sayHi(); // the compiler binds any invocation of sayHi()
// to sayHi()’s entry point.
}
void sayHi(){
cout << ‘‘Hello, World!\n’’;
}
In C, only compile-time binding is provided.
In C++, all non-virtual functions are bound at
compile-time.
1
Polymorphism
Run-time binding ( Dynamic binding )
• Run-time binding is to associate a function’s name
with the entry point at run-time.
• C++ supports run-time binding through virtual
functions.
• Polymorphism is thus implemented by virtual
functions and run-time binding mechanism in
C++. A class is called polymorphic if it
contains virtual functions.
2
Polymorphism
A typical scenario of polymorphism in C++:
• There is an inheritance hierarchy
• The first class that defines a virtual function is the
base class of the hierarchy that uses dynamic
binding for that function name and signature.
• Each of the drived classes in the hierarchy must
have a virtual function with same name and
signature.
• There is a pointer of base class type; and this
pointer is used to invoke virtual functions of
derived class.
3
Polymorphism
Example:
#include <iostream>
using namespace std;
class Shape{
public:
virtual void sayHi() { cout <<‘‘Just hi! \n’’;}
};
class Triangle : public Shape{
public:
virtual void sayHi() { cout <<‘‘Hi from a triangle! \n’’;}
};
class Rectangle : public Shape{
public:
virtual void sayHi() { cout <<‘‘Hi from a rectangle! \n; }
};
int main(){
Shape *p;
int which;
cout << ‘‘1 -- shape, 2 -- triangle, 3 -- rectangle\n ’’;
cin >> which;
switch ( which ) {
case 1: p = new Shape; break;
case 2: p = new Triangle; break;
case 3: p = new Rectangle; break;
}
p -> sayHi(); // dynamic binding of sayHi()
delete p;
}
4
Polymorphism
Virtual Functions
• To declare a function virtual, we use the Keyword
virtual.
class Shape{
public:
virtual void sayHi (){ cout <<‘‘Just hi! \n’’;}
};
• If the member function definition is outside the
class, the keyword virtual must not be specified
again.
class Shape{
public:
virtual void sayHi ();
};
virtual void Shape::sayHi (){ // error
cout << ‘‘Just hi! \n’’;
}
• Virtual functions can not be stand-alone functions
or static methods.
5
Polymorphism
• A virtual function can be used same as non-virutal
member functions.
Example:
class B {
public:
virtual void m() { cout << ‘‘Hello! \n’’; }
};
int main(){
B b_obj;
b_obj.m();
}
• A virtual function can be inherited from a base
class by a derived class, like other class member
functions.
Example:
class B {
public:
virtual void m() { cout << ‘‘Hello! \n’’;}
};
class D : public B{
// inherite B::m()
};
int main(){
D d_obj;
d_obj.m();
}
6
Polymorphism
To let derived classes have their own implementation for
the virtual function. we override base class virtual
functions in derived class.
In order for a derived class virtual function instance to
override the base class virtual function instance, its
signature must match the base class virtual function
exactly.
The overriding functions are virtual automatically. The
use of keyword virtual is optional in derived classes.
Example:
class Shape{
public:
virtual void sayHi() { cout <<‘‘Just hi! \n’’;}
};
class Triangle : public Shape{
public:
// overrides Shape::sayHi(), automatically virtual
void sayHi() { cout <<‘‘Hi from a triangle! \n’’;}
};
7
Polymorphism
Polymorphism
Dynamic binding is enabled when a virtual
function is invoked through a derived class
object which is refered indirectly by either a base
class pointer or reference,
Example:
void print(Shape obj, Shape *ptr, Shape &ref){
ptr -> sayHi(); // bound at run time
ref.sayHi(); // bound at run time
obj.sayHi(); // bound at compile time
}
int main(){
Triangle mytri;
print( mytri, &mytri, mytri );
}
Thus, polymorphism in C++ is supported by
using pointers and references.
8
Polymorphism
Exercise 1:
Is m() bound at compile time or run time ?
Output ?
class B {
public:
void m() { cout << ‘‘B::m \n’’;}
};
class D : public B{
public:
void m() { cout << ‘‘D::m \n’’;}
};
int main(){
B *p;
p = new D;
p -> m();
}
9
Polymorphism
Exercise 2:
Is m() bound at compile time or run time ?
Output?
class B {
public:
virtual void m() { cout << ‘‘B::m \n’’;}
};
class D : public B{
public:
void m() { cout << ‘‘D::m \n’’;}
};
int main(){
D d;
d.m();
}
10
Polymorphism
Name Hiding
When derived class adds a method with the
same name but different signature to the base
class virtual function, this new method hides the
base class virtual function.
Example:
class B {
public:
virtual void m(int x){ cout << ‘‘B::m \n’’;}
};
class D : public B{
public:
void m(){ cout << ‘‘D::m \n’’;} // hides B::m(int)
};
int main(){
D d;
d.m(5); // Error! B::m(int) is hidden.
}
11
Polymorphism
Exercise 3:
Is m() bound at compile time or run time ?
class B {
public:
virtual void m(int x){ cout << ‘‘B::m \n’’;}
};
class D : public B{
public:
virtual void m(){ cout << ‘‘D::m \n’’;}
};
int main(){
B *p;
p = new D;
p -> m();
}
12
Polymorphism
Static Invocation of Virtual Functions
We can override the virtual mechanism when
using the class scope operator to invoke a virtual
function. Thus, the virtual function is resolved
at compile-time.
Example:
class B {
public:
virtual void m(){ cout << ‘‘B::m \n’’;}
};
class D : public B{
public:
void m(){ cout << ‘‘D::m \n’’;}
};
int main(){
B *p = new D;
p -> B::m();
}
13
Polymorphism
Virtual Tables
C++ uses the virtual table (vtable) machanism
to implement the dynamic binding of virtual
functions.
• A class with virtual member functions has a virtual
table which contains the address of its virtual
functions.
• An object of such a class has a pointer(vptr) to
point to the virtual table of the class.
• Dynamic binding is done by looking up the virtual
table for the entry point of the appropriate
function at run-time.
14
Polymorphism
Example:
class B {
public:
virtual void m1(){
// ...
}
virtual void m2(){
// ...
}
};
class D :: B {
void m1(){ // overide B::m1()
// ...
}
};
int main(){
B *p;
B b;
D d;
p = &d; // p is set to d’s address
p -> m1();
p -> m2();
p = &b; // p is set to b’s address
p -> m1()
p -> m2();
}
15
Polymorphism
Constructors and Destructors
• A constructor cannot be virtual since it is used to
construct an object.
• A destructor can be virtual. Virtual destructors
are very useful when some derived classes have
cleanup code.
Example:
class B {
public:
virtual B(); // error
virtual ~B(); // ok
virtual void f(); // ok
};
16
Polymorphism
Example:
class B{
public:
B(){
cout <<"constructing B. \n";
bp = new char[5];
}
~B(){
cout <<"destructing B. \n";
delete[] bp;
}
private:
char *bp;
};
class D : public B{
public:
D(){
cout <<"constructing D. \n";
dp = new char[5000];
}
~D(){
cout <<"destructing D. \n";
delete[] dp;
}
private:
char *dp;
};
int main(){
B *ptr = new D();
delete ptr;
}
OUTPUT?
17
Polymorphism
Fix the problem by using a virtual destructor.
class B{
public:
// ...
virtual ~B(){
cout <<"destructing B. \n";
delete[] bp;
}
// ...
};
class D : public B{
// ...
};
int main(){
B *ptr = new D();
delete ptr;
}
When the destructor of base class is made
virtual, destructors of derived classes are virtual
automatically. Thus, run-time binding is in
effect.
18
Polymorphism
Run-time v.s. compile-time binding
• The approach of using inheritance and run-time
binding facilitates the following software quality
factors:
– Reuse
– Transparent extensibility
– Delaying decisions until run-time
– Architectural simplicity
• Compared to compile time binding, run time
binding has overhead in terms of space and time.
– Extra space is needed for virtual table.
– Extra time for virtual table lookup is required
at each polymorfic function call.
19
Polymorphism
When to choose use different kinds of bindings:
• Use compile-time binding when you are sure that
any derived class will not want to override the
function dynamically.
• Use run-time binding when the derived class may
be able to provide a different implementation that
should be selected at run-time.
20
Polymorphism
Example:
class Shape {
public:
void setDim(double, double = 0);
virtual void showArea();
protected:
double x, y;
};
void Shape::setDim(double xx, double yy) : x(xx), y(yy){}
void Shape::showArea(){
cout << "No area computation defined for this class\n";
}
In base class Shape:
• setDim() is a non-virtual member function since
its operation is common to all derived classes.
• showArea() is declared virtual since the area of
each object is computed differently.
21
Polymorphism
// Derived class Triangle from Shape
class Triangle : public Shape{
public:
virtual void showArea();
};
void Triangle::showArea(){
cout << "Triangle with height " << x << " and base " << y
<< " has an area of " << x * y* 0.5 << endl;
}
// Derived class Rectangle from Shape
class Rectangle : public Shape{
public:
virtual void showArea();
};
void Rectangle::showArea(){
cout << "Rectangle with dimentions " << x << " and " << y
<< " has an area of " << x * y << endl;
}
// Derived class Circle from Shape
class Circle : public Shape{
public:
virtual void showArea();
};
void Circle::showArea(){
cout << "Circle with radius " << x
<< " has an area of " << 3.14 * x * x << endl;
}
22
Polymorphism
int main(){
Shape *ptr; // declare a pointer to base class
Shape myshape; // create objects
Triangle t;
Rectangle s;
Circle c;
ptr = &myshape;
ptr -> showArea();
ptr = &t;
ptr -> setDim(10.0, 5.0);
ptr -> showArea();
ptr = &s;
ptr -> setDim(10.0, 10.0);
ptr -> showArea();
ptr = &c;
ptr -> setDim(10.0);
ptr -> showArea();
}
23
Polymorphism
Pure Virtual Function
• A pure virtual function is a virtual function in base
class that has no definition.
E.g. Consider the virtual function showArea() in
base class Shape; it has only an abstract meaning.
Thus, showArea() can be declared as pure virtual
function.
• A pure virtual function is declared using
specifier “= 0 ”.
24
Polymorphism
Note:
• Only a virtual member function can be pure.
void f() = 0; //error! f() is a stand alone function
class B{
public:
void setX() = 0; //error! setX not virtual
// ...
};
• Declaring a virtual function pure is not the same as
defining a virtual function with an empty body.
class B{
public:
virtual void setX(){} // virtual but not pure
// ...
};
25
Polymorphism
Abstract Classes
• A class that has a pure virtual function is an
abstract class. Abstract class is used as an
interface for its derived classes.
• If a class derived from an abstract class, and this
class doesn’t override all the pure virtual funtion in
the base class, then this class is also an abstract
class.
• No object can be created for an abstract class !
Therefore, classes derived from an abstract class
must override all of the base class’s pure virtual
functions to become “non-abstract”.
26
Polymorphism
Example:
Since class Shape has a pure virtual function, it
becomes an abstract base class (ABC).
Now, class Triangle must override
showArea().
class Shape {
public:
void setDim(double, double = 0);
virtual void showArea() = 0; // pure
protected:
double x, y;
};
class Triangle : public Shape{
// must override Shape::showArea()
// ...
};
27
Polymorphism
Example (Cont’d)
class Shape { // Abstract base class
public:
void setDim(double, double = 0);
virtual void showArea() = 0;
protected:
double x, y;
};
void Shape::setDim(double xx, double yy){
x = xx;
y = yy;
}
class Triangle : public Shape{
public:
virtual void showArea(); // a must !
};
void Triangle::showArea(){
cout << "Triangle with height " << x << " and base " << y
<< " has an area of " << x * y* 0.5 << "\n";
}
class Rectangle : public Shape{
public:
virtual void showArea(); // a must !
};
void Rectangle::showArea(){
cout << "Rectangle with dimentions " << x << " and " << y
<< " has an area of " << x * y << "\n";
}
// ... class Circle
28
Polymorphism
int main(){
Shape *ptr; // pointers to ABC is ok.
Shape myshape; // wrong !
Triangle t;
Rectangle s;
Circle c;
ptr = &t;
ptr -> setDim(10.0, 5.0);
ptr -> showArea();
ptr = &s;
ptr -> setDim(10.0, 10.0);
ptr -> showArea();
ptr = &c;
ptr -> setDim(10.0);
ptr -> showArea();
}
29
Polymorphism
Virtual Multiple Inheritance
class A { ... };
class B : public A { ... };
class C : public A { ... };
class D : public B, public C { ... };
v.s.
class A { ... };
class B : virtual public A { ... };
class C : virtual public A { ... };
class D : public B, public C { ... };
30
Polymorphism
Run-Time Type Checking
C++ supports run-time type
identification(RTTI). It provides mechanisms to
• Check type conversion at run time.
• Determine the actual derived object’s type that a
pointer(or reference) refers to at run time.
Two operators are provided for RTTI support:
• dynamic cast operator
• typeid operator
Used only for polymorphic types, e.g. types with
virtual-functions.
31
Polymorphism
A pointer of derived class can be assigned to a
pointer of base class, which is known as upcast.
A pointer of base class can not be assigned to a
pointer of derived class, which is known as
downcast.
Example:
class B{
public:
int zip() { return x; }
private:
int x;
};
class D : public B{
public:
int zap() { return y; }
private:
int y;
};
int main(){
D *dptr;
B *bptr;
bptr = dptr; // ok, upcast
dptr = bptr; // compile time error
// Cannot assign B* to D* downcast
dptr = static_cast< D* >( bptr ); // ?
}
Downcast might not be safe, but can not be detected by the
compiler.
32
Polymorphism
static cast is not type-safe. Run-time error
may occur.
Example:
int main(){
D *dptr;
B *bptr = new B;
dptr = static_cast< D* >( bptr );
dptr -> zap(); // ?
}
33
Polymorphism
C++ provides dynamic cast for safe type
conversion.
class B{
public:
virtual int zip() { return x; }
private:
int x;
};
class D : public B{
// ...
};
int main(){
D *dptr;
B *bptr = new B;
dptr = dynamic_cast< D* >( bptr );
if (dptr) // check if cast is successful
dptr -> zap();
else
cerr << "Cast not safe \n";
}
dynamic cast is legal only on a polymorphic
type.
dynamic cast performs two operations at once.
First, it verifies that the cast is valid. Then only
if the cast is valid does it perform the cast,
otherwise it returns a null pointer.
34
Polymorphism
dynamic cast provides an alternative to the
virtual function machanism.
#include <iostream>
using namespace std;
class Employee{
public:
virtual void salary() { cout << "Employee::salary"; }
};
class Manager : public Employee{
public:
void salary() { cout << "Manager::salary"; }
};
class Programmer : public Employee{
public:
void salary() { cout << "Programmer::salary"; }
void bonus() { cout << "Programmer::bonus"; }
};
void paycheck( Employee *ep ){
Programmer *pp = dynamic_cast< Programmer* >( ep );
if (pp)
pp -> bonus();
else
ep -> salary();
}
int main(){
Employee *eptr = new Programmer;
paycheck( eptr );
eptr = new Manager;
paycheck( eptr );
}
35
Polymorphism
C++ also provides a typeid operator that
allows queries of type information at run-time.
Example:
#include <iostream>
#include <typeinfo>
using namespace std;
class B_class{
public:
virtual void f(){}
// ...
};
class D_class : public B_class{
// ...
};
int main(){
int x;
cout << typeid( x ).name() << endl;
cout << typeid( 8.16 ).name() <<endl;
D_class dobj;
B_class *bptr = &dobj;
cout << typeid( bptr ).name() << endl;
cout << typeid( *bptr ).name() << endl;
}
36
Polymorphism
The result of the typeid operator can be
compared.
Example:
B_class *bptr = new D_class;
typeid( bptr ) == typeid( B_class* ) // true
typeid( bptr ) == typeid( D_class* ) // false
typeid( bptr ) == typeid( B_class ) // false
typeid( bptr ) == typeid( B_class ) // false
typeid( *bptr ) == typeid( D_class ) // true
typeid( *bptr ) == typeid( B_class ) // false
The typeid operator is used for advanced
system programming.
37