Virtual Functions and Polymorphism

Programming Workshop 2 (CSCI 1061U)

Faculty of Science, UOIT

http://vclab.science.uoit.ca

Virtual functions and Polymorphism

Polymorphism is a key principle of object-oriented programming. It enables us to assocaite many meanings to a single function. Virtual functions provide this capability.

Consider the following `shapes.h` and `shapes.cpp` files that implement three classes: `Shape`, `Circle`, and `Rectangle`. Classes `Circle` and `Rectangle` are inherited from class `Shape`.

shapes.h

``````class Shape {
public:
Shape();
~Shape();
void draw(); // draws an empty shape
};

class Rectangle : public Shape
{
public:
Rectangle(int w, int h);
~Rectangle();
void draw(); // draws a rectangle

private:
int _w, _h; // width & height
};

class Circle : public Shape
{
public:
Circle(int r);
~Circle();
void draw(); // draws a circle

private:
};``````

shapes.cpp

``````#include "Shapes.h"
#include <iostream>
using namespace std;

Shape::Shape() {}

Shape::~Shape() {}

void Shape::draw()
{
cout << "Drawing an empty shape" << endl;
}

/////////////////////////////////////////////
Rectangle::Rectangle(int w, int h)
: _w(w), _h(h)
{}

Rectangle::~Rectangle() {}

void Rectangle::draw()
{
cout << "Drawing a rectangle of width " << _w
<< " and height " << _h
<< endl;
}

/////////////////////////////////////////////
Circle::Circle(int r)
: _r(r)
{}

Circle::~Circle() {}

void Circle::draw()
{
cout << "Drawing a circle of radius " << _r << endl;
}``````

Now lets consider the following `main.cpp` file.

main.cpp

``````#include "Shapes.h"

int main()
{
Circle* c = new Circle(2);
c->draw(); // this draws circle

Rectangle* r = new Rectangle(3,6);
r->draw(); // this draws rectangle

delete c; delete r;
}``````

If we compile and run this program we get

``````> g++ main.cpp shapes.cpp -o ex1
> ./ex1
Drawing a circle of radius 2
Drawing a rectangle of width 3 and height 6``````

This is as we expected.

Now lets consider another situation. Consider the following program that stores a list of shapes in an array.

main2.cpp

``````#include "Shapes.h"

int main()
{
Shape* s; // a list of different kind of shapes
s = new Circle(4);
s = new Rectangle(4,5);

for (int i=0; i<2; ++i) {
s[i]->draw();
}

for (int i=0; i<2; ++i) {
delete s[i];
}
}``````

We know that an array only store elements of the same type. The array `s` in the above code stores pointers to `Shape`. We assign a pointer to a `Circle` instance to `s` and a pointer to a `Rectangle` instance to `s`. This is allowed since both `Circle` and `Rectangle` are subclasses of `Shape`. Recall that `Circle` is a `Shape`, and `Rectangle` is a `Shape`.

However, when we compile and run the above program, we get the following

``````> g++ main2.cpp shapes.cpp -o ex2
> ./ex2
Drawing an empty shape
Drawing an empty shape``````

It seems that the `s[i]->draw()` isn't behaving as we expected. Obviously, the `draw` function in class `Shape` is being called. We want the `draw` functions of the respective classes to be called. `s->draw()` should call the `draw` function in `Circle` class. Similarly `s->draw()` should call the `draw` function in `Rectangle` class. How do we do it?

We can use late binding to discern the actual type of the pointer and then call the appropriate function. So, we can discern that `s` is actually a pointer to a `Circle` instance. In C++, we achieve this be declaring a function virtual. Specifically, we will modify the parent class as follows

``````class Shape {
public:
Shape();
~Shape();
virtual void draw(); // draws an empty shape
};``````

By declaring the `draw` function virtual in the parent class, we assert that function implementation is not known at compile time. Furthermore, that function implementation should be inferred from object instance at runtime. Note that in C++ late binding is performed only for functions declared virtual.

If we compile and execute the program now, we get the correct behavior

``````> g++ main2.cpp shapes.cpp -o ex2
> ./ex2
Drawing a circle of radius 4
Drawing a rectangle of width 4 and height 5``````

Overriding

If virtual function definition is changed in the derived class, we say that it has been overridden. When a non-virtual function is changed in the derived class, we say that the function has been redefined.

C++11 includes `override` keyword that makes it clear whether a function overrides a function in the parent class.

``````class Sale:
{
public:
virtual double bill() const;
};

class DiscountSale: public Sale
{
public:
double bill() const override;
};``````

C++11 `final` keyword

C++11 includes the final keyword to prevent a function from being overridden. Useful if a function is overridden but don’t want a derived classes to override it again.

``````class Sale:
{
public:
virtual double bill() const final;
};

class DiscountSale: public Sale
{
public:
double bill() const; // This will give a compiler error
};``````

Pure virtual function

Base classes may not have a meaningful definitions for some of its functions. The purpose of the base class is solely for others to derive from it. It is possible to create a pure virtual function as follows

``````class Shape
{
public:
virtual draw() = 0; // Pure virtual function
};``````

This has two consquences. First, it is not possible to create an instance of `Shape` class. `Shape` class is now an abstract class. We cannot create an instance of `Shape` class, since it is missing definition of one of its functions. Second, it a derived (from `Shape`) class must implement `draw`. If the derived class doesn't implmenet `draw` the the derived class is also an abstract class.