[go: up one dir, main page]

0% found this document useful (0 votes)
76 views38 pages

Oop 07

Polymorphism in C++ allows objects of derived classes to be treated as objects of base classes. This is achieved through virtual functions and dynamic binding at runtime. Virtual functions allow derived classes to override the implementation in base classes. When a virtual function is called through a base class pointer, the version of the function that is called is determined by the actual object type at runtime rather than its declared type. [/SUMMARY]

Uploaded by

Adnan Rai
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
76 views38 pages

Oop 07

Polymorphism in C++ allows objects of derived classes to be treated as objects of base classes. This is achieved through virtual functions and dynamic binding at runtime. Virtual functions allow derived classes to override the implementation in base classes. When a virtual function is called through a base class pointer, the version of the function that is called is determined by the actual object type at runtime rather than its declared type. [/SUMMARY]

Uploaded by

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

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

You might also like