Upcasting and Downcasting in C++ with Virtual Functions

In this tutorial we will focus on Upcasting and Downcasting in C++, and since their use depends on Virtual Functions, we will be discussing those as well.

Virtual functions are a very powerful tool in C++, without which many things would not possible. While Function overriding is similar to Virtual Functions, there is one major difference between the two. And that is when Upcasting and Downcasting in C++ is being used.


Casting in C++

“Casting” is a term widely used in programming, used when converting objects of one type to another. It’s basically a “forced conversion”, where you manually convert one type to another.

A small example:

float b = 4.53;
int c;

c = (int) b;

Even though c is an int, we were able to assign it a value from b by casting it to an int. What happens is that is the float value, 4.53 loses all digits after the decimal point and gets converted to an int.

We’ll be expanding on this concept later, when it comes to casting between Child and Parent Classes in C++.


Below are the Classes that we are going to be using.

C++ Upcasting and Downcasting

And here are the Class definitions. Take a good look at them before proceeding onwards, because we’ll be working with these for the rest of the tutorial.

class Employee {
public:
    string name;

    virtual void display() {
        cout << "I am an Employee" << endl;
    }
};

class Manager : public Employee{
    string branch;

public:
    void display() {
        cout << "I am an Manager" << endl;
    }
};

class Receptionist : public Employee {
public:
    int lobbyNumber;

    void Lobby() {
        cout << "Lobby: " << lobbyNumber << endl;
    }
};

Upcasting in C++

Upcasting is something that occurs automatically, without us having to manually cast it. It occurs when we create a Base Class pointer, and treat it like a pointer to the Child Class.

In short, Upcasting occurs when we attempt to cast a Child to a Parent, “Up” the Hierarchy. Downcasting is the exact opposite.

This allows us to be able to access the functions within the Child class, which the Base Class Pointer should not be able to access normally. Of course, we must have the virtual keyword enabled on the function(s) of the Base class, otherwise the effect will be different.

#include <iostream>
using namespace std;


class Employee {
public:
    string name;

    virtual void display() {
        cout << "I am an Employee" << endl;
    }
};

class Manager : public Employee{
    string branch;

public:
    void display() {
        cout << "I am an Manager" << endl;
    }
};

class Receptionist : public Employee {
public:
    int lobbyNumber;

    void Lobby() {
        cout << "Lobby: " << lobbyNumber << endl;
    }
};

int main() {
    Manager m1;
    m1.name = "Mark";

    Employee* e1 = &m1;  // Upcasting

    e1->display();       // Base Class Pointer
}

The above example will cause the following line to be printed out:

I am an Manager

However, if we remove the virtual keyword, then when we call the display function using the Base Class pointer (e1), then the display() function of the Base Class will be called. Instead of the display() function on the Child Class.


Just to be clear, Upcasting and Downcasting is an independent topic. We are just using Virtual Functions here to take things a step further and show you how we can use Upcasting effectively. Otherwise the concept of Upcasting/Downcasting ends once the Cast has been completed.


Downcasting in C++

The exact opposite of Upcasting, Downcasting occurs when we cast downwards from Parent to Child.

Downcasting is not as safe as Upcasting, hence it will thrown an error if an implicit conversion is attempted. In order to perform Downcasting, you need to forcefully cast it. The reason for this limitation is simple.

A Child Class, such as a Manager, will always an Employee. However, the Employee will not always be a Manager. For example, the Receptionist and the Manager Sub-Classes both have unique variables that the Employee doesn’t have.

Below is an example of an explicit Downcast. This can be risky because we can call the Lobby() method, which uses the lobbyNumber variable in Receptionist, not present in Employee. This can lead to unexpected results.

int main() {
    Employee e1;
    e1.name = "Bob";

    Receptionist* r1 = (Receptionist*)(&e1);
    r1->Lobby();
}

Try running it for yourself and see the result.


Dynamic Cast

This is a safer way of Downcasting, that first checks to make sure the reference in e1 is even valid. If it’s not valid, it will return an exception.

One important thing to keep in mind, is that dynamic_cast requires polymorphism. In short, without the virtual keyword it will not work.

int main() {
    Employee e1;
    e1.name = "Bob";

    Receptionist *r1 = dynamic_cast<Receptionist*>(&e1);
}

Normally, without Dynamic Casting, the cast might go ahead and unexpected behavior could occur without your knowledge.


Function Over-riding vs Virtual Functions

The difference between Function over-riding and virtual functions, is when Upcasting/Downcasting is used. If you have a function in the Base Class, which you have re-declared in the Child Class, that’s Function over-riding. This will not call the Child Class function when using the Base Class pointer.

But, if you do the exact same thing, and just add the Virtual Keyword, this will give the intended effect that we have demonstrated in this tutorial.


This marks the end of the Upcasting and Downcasting in C++ Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments