Upcasting and Downcasting in C++

Introduction

Upcasting and downcasting are an important part of C++. Upcasting and downcasting give a possibility to build complicated programs with a simple syntax. It can be achieved by using Polymorphism.

C++ allows that a derived class pointer (or reference) to be treated as a base class pointer. This is upcasting.

Downcasting is an opposite process, which consists of converting base class pointer (or reference) to derived class pointer.

C++ Upcasting and Downcasting should not be understood as a simple casting of different data types. It can lead to great confusion.

In this topic, we will use the following hierarchy of classes:

C++ Upcasting and Downcasting

As you can see, Manager and Clerk are both Employee. They are both Person too. What does it mean? It means that Manager and Clerk classes inherit properties of Employee class, which inherits properties of Person class.

For example, we don’t need to specify that both Manager and Clerk are identified by First and Last name, have a salary; you can show information about them and add a bonus to their salaries. We have to specify these properties only once in the Employee class:

At the same time, the Manager and Clerk classes are different. The Manager takes a commission fee for every contract, and the Clerk has information about his Manager:

Try It

#include <iostream>
using namespace std;

class Person
{
	//content of Person
};


class Employee:public Person
{
public:
	Employee(string fName, string lName, double sal)
	{
		FirstName = fName;
		LastName = lName;
		salary = sal;
	}
	string FirstName;
	string LastName;
	double salary;
	void show()
	{
		cout << "First Name: " << FirstName << " Last Name: " << LastName << " Salary: " << salary<< endl;
	}
	void addBonus(double bonus)
	{
		salary += bonus;
	}
};

class Manager :public Employee
{
public:
	Manager(string fName, string lName, double sal, double comm) :Employee(fName, lName, sal)
	{
		Commision = comm;
	}
	double Commision;
	double getComm()
	{
		return Commision;
	}
};

class Clerk :public Employee
{
public:
	Clerk(string fName, string lName, double sal, Manager* man) :Employee(fName, lName, sal)
	{
		manager = man;
	}
	Manager* manager;
	Manager* getManager()
	{
		return manager;
	}
};

void congratulate(Employee* emp)
{
	cout << "Happy Birthday!!!" << endl;
	emp->addBonus(200);
	emp->show();
};

int main()
{
    //pointer to base class object
    Employee* emp;
    
    //object of derived class
    Manager m1("Steve", "Kent", 3000, 0.2);
    Clerk c1("Kevin","Jones", 1000, &m1);
    
    //implicit upcasting
    emp = &m1;
    
    //It's ok
    cout<<emp->FirstName<<endl;
    cout<<emp->salary<<endl;
    
    //Fails because upcasting is used
    //cout<<emp->getComm();
    
    congratulate(&c1);
    congratulate(&m1);
    
    cout<<"Manager of "<<c1.FirstName<<" is "<<c1.getManager()->FirstName;
}

 

Manager and Clerk are always Employees. Moreover, Employee is a Person. Therefore, the Manager and Clerk are Persons too. You have to understand it before we start learning upcasting and downcasting.

Both upcasting and downcasting do not change the object by itself. When you use upcasting or downcasting you just “label” an object in different ways.

UPCASTING

Upcasting is a process of creating a pointer or a reference of the derived class object as a base class pointer. You do not need to upcast manually. You just need to assign derived class pointer (or reference) to base class pointer:

//pointer to base class object
Employee* emp;
//object of derived class
Manager m1("Steve", "Kent", 3000, 0.2);
//implicit upcasting
emp = &m1;

When you use upcasting, the object is not changing. Nevertheless, when you upcast an object, you will be able to access only member functions and data members that are defined in the base class:

//It's ok
emp->FirstName;
emp->salary;
//Fails because upcasting is used
emp->getComm();

Example of upcasting usage

One of the biggest advantages of upcasting is the capability of writing generic functions for all the classes that are derived from the same base class. Look at an example:

void congratulate(Employee* emp)
{
	cout << "Happy Birthday!!!" << endl;
	emp->show();
	emp->addBonus(200);
};

This function will work with all the classes that are derived from the Employee class. When you call it with objects of type Manager and Person, they will be automatically upcasted to Employee class:

//automatic upcasting
congratulate(&c1);
congratulate(&m1);

Try to run this program:

Happy Birthday!!!
First Name: Kevin Last Name: Jones
Happy Birthday!!!
First Name: Steve Last: Name Kent

An example of how to use upcasting with virtual functions is described in the “C++ Polymorphism” topic.

Memory layout

As you know, the derived class extends the properties of the base class. It means that derived class has properties (data members and member functions) of the base class and defines new data members and member functions.

Look on the memory layout of the Employee and Manager classes:

C++ Upcasting and Downcasting

Of course, this model is a simplified view of memory layout for objects. However, it represents the fact that when you use a base class pointer to point up an object of the derived class, you can access only elements that are defined in the base class (green area). Elements of the derived class (yellow area) are not accessible when you use a base class pointer.

DOWNCASTING

Downcasting is an opposite process for upcasting. It converts base class pointer to derived class pointer. Downcasting must be done manually. It means that you have to specify explicit typecast.

Downcasting is not as safe as upcasting. You know that a derived class object can be always treated as a base class object. However, the opposite is not right. For example, a Manager is always a Person; But a Person is not always a Manager. It could be a Clerk too.

You have to use an explicit cast for downcasting:

//pointer to base class object
Employee* emp;
//object of derived class
Manager m1("Steve", "Kent", 3000, 0.2);
//implicit upcasting
emp = &m1;
//explicit downcasting from Employee to Manager
Manager* m2 = (Manager*)(emp);

This code compiles and runs without any problem because emp points to an object of Manager class.

What will happen, if we try to downcast a base class pointer that is pointing to an object of the base class and not to an object of derived class? Try to compile and run this code:

Employee e1("Peter", "Green", 1400);
//try to cast an employee to Manager
Manager* m3 = (Manager*)(&e1);
cout << m3->getComm() << endl;

e1 object is not an object of the Manager class. It does not contain any information about the commission. That’s why such an operation can produce unexpected results.

Look on the memory layout again:

C++ Upcasting and Downcasting

When you try to downcast base class pointer (Employee) that is not actually pointing up an object of the derived class (Manager), you will get access to the memory that does not have any information about the derived class object (yellow area). This is the main danger of downcasting.

You can use a safe cast that can help you to know if one type can be converted correctly to another type. For this purpose, use a dynamic cast.

Dynamic Cast

dynamic_cast is an operator that converts safely one type to another type. In the case, the conversation is possible and safe, it returns the address of the object that is converted. Otherwise, it returns nullptr.

dynamic_cast has the following syntax

	
dynamic_cast<new_type> (object)

If you want to use a dynamic cast for downcasting, the base class should be polymorphic – it must have at least one virtual function. Modify base class Person by adding a virtual function:

virtual void foo() {}

Now you can use downcasting for converting Employee class pointers to derived classes pointers.

Employee e1("Peter", "Green", 1400);
Manager* m3 = dynamic_cast<Manager*>(&e1);
if (m3)
	cout << m3->getComm() << endl;
else
	cout << "Can't  cast from Employee to Manager" << endl;

In this case, the dynamic cast returns nullptr. Therefore, you will see a warning message.

In this article, we have read about C++ Upcasting and Downcasting. In next articles, we will cover more topics on C++.

Reference

Translate »