Bogotobogo
contact@bogotobogo.com


C++ Tutorial - Dynamic Cast - 2012
Bookmark and Share
cplusplus logo
Full List of C++ Tutorials




ImsilOkJungHo3

RTTI

RTTI is short for Run-time Type Identification. RTTI is to provide a standard way for a program to determine the type of object during runtime.

In other words, RTTI allows programs that use pointers or references to base classes to retrieve the actual derived types of the objects to which these pointers or references refer.

RTTI is provided through two operators:

  • The typeid operator, which returns the actual type of the object referred to by a pointer (or a reference).
  • The dynamic_cast operator, which safely converts from a pointer (or reference) to a base type to a pointer (or reference) to a derived type.




The dynamic_cast Operator

An attempt to convert an object into a more specific object.

The dynamic_cast operator is intended to be the most heavily used RTTI component. It doesn't give us what type of object a pointer points to. Instead, it answers the question of whether we can safely assign the address of an object to a pointer of a particular type.

Unlike other casts, a dynamic_cast involves a run-time type check. If the object bound to the pointer is not an object of the target type, it fails and the value is 0. If it's a reference type when it fails, then an exception of type bad_cast is thrown. So, if we want dynamic_cast to throw an exception (bad_cast) instead of returning 0, cast to a reference instead of to a pointer. Note also that the dynamic_cast is the only cast that relies on run-time checking.


Your Ad Here

"The need for dynamic_cast generally arises because you want to perform derived class operation on a derived class object, but you have only a pointer or reference-to-base" said Scott Meyers in his book "Effective C++".

Let's look at the example code:

class Base { };

class Derived : public Base { };

int main() 
{
	Base b;
	Derived d;

	Base *pb = dynamic_cast<Base*>(&d);      	// #1
	Derived *pd = dynamic_cast<Derived*>(&b); 	// #2

	return 0;
}

The #1 is ok because dynamic_cast is always successful when we cast a class to one of its base classes

The #2 conversion has a compilation error:

 error C2683: 'dynamic_cast' : 'Base' is not a polymorphic type.

It's because base-to-derived conversions are not allowed with dynamic_cast unless the base class is polymorphic.

So, if we make the Base class polymorphic by adding virtual function as in the code sample below, it will be compiled successfully.

class Base {virtual void vf(){}};

class Derived : public Base { };

int main() 
{
	Base b;
	Derived d;

	Base *pb = dynamic_cast<Base*>(&d);		// #1
	Derived *pd = dynamic_cast<Derived*>(&b); 	// #2

	return 0;
}

But at runtime, the #2 cast fails and produces null pointer.

Let's look at another example.

class Base { virtual void vf(){} };

class Derived : public Base { };

int main() 
{
	Base *pBDerived = new Derived;
	Base *pBBase = new Base;
	Derived *pd;

	pd = dynamic_cast<Derived*>(pBDerived);	#1
	pd = dynamic_cast<Derived*>(pBBase);	#2 

	return 0;
}

The example has two dynamic casts from pointers of type Base to a point of type Derived. But only the #1 is successful.

Even though pBDerived and pBBase are pointers of type Base*, pBDerived points to an object of type Derived, while pBBase points to an object of type Base. Thus, when their respective type-castings are performed using dynamic_cast, pBDerived is pointing to a full object of class Derived, whereas pBBase is pointing to an object of class Base, which is an incomplete object of class Derived.

In general, the expression

dynamic_cast<Type *>(ptr)
converts the pointer ptr to a pointer of type Type* if the pointer-to object (*ptr) is of type Type or else derived directly or indirectly from type Type. Otherwise, the expression evaluates to 0, the null pointer.



Upcasting and Downcasting

Converting a derived-class reference or pointer to a base-class reference or pointer is called upcasting. It is always allowed for public inheritance without the need for an explicit type cast.

Actually this rule is part of expressing the is-a relationship. A Derived object is a Base object in that it inherits all the data members and member functions of a Base object. Thus, anything that we can do to a Base object, we can do to a Derived class object.

The downcasting, the opposite of upcasting, is a process converting a base-class pointer or reference to a derived-class pointer or reference.

It is now allowed without an explicit type cast. A derived class could add new data members, and the class member functions that used these data members wouldn't apply to the base class.

Here is a self explanatory example

#include <iostream>
using namespace std;

class Employee { 
private:
	int id;
public:
	void show_id(){}
};

class Programmer : public Employee {
public:
	void coding(){}
};

int main() 
{
	Employee employee;
	Programmer programmer;

	// upcast - implicit upcast allowed
	Employee *pEmp = &programmer;

	// downcast - explicit type cast required
	Programmer *pProg = (Programmer *)&employee;


	// Upcasting: safe - progrommer is an Employee 
	// and has his id to do show_id().
	pEmp->show_id();
	pProg->show_id();

	// Downcasting: unsafe - Employee does not have
	// the method, coding().
       // compile error: 'coding' : is not a member of 'Employee'
	// pEmp->coding(); 
	pProg->coding();

	return 0;
}

More on Upcasting and Downcasting



The typeid

typeid operator allows us to determine whether two objects are the same type.

In the previous example for Upcasting and Downcasting, employee gets the method coding() which is not desirable. So, we need to check if a pointer is pointing to the Programmer object before we use the method, coding().

Here is a new code showing how to use typeid:

class Employee { 
private:
	int id;
public:
	void show_id(){}
};

class Programmer : public Employee {
public:
	void coding(){}
};

#include <typeinfo>

int main() 
{
	Employee lee;
	Programmer park;

	Employee *pEmpA = &lee;
	Employee *pEmpB = &park;

	// check if two object is the same
	if(typeid(Programmer) == typeid(lee)) {
		Programmer *pProg = (Programmer *)&lee; 
		pProg->coding();
	}
	if(typeid(Programmer) == typeid(park)) {
		Programmer *pProg = (Programmer *)&park; 
		pProg->coding();
	}

	pEmpA->show_id();
	pEmpB->show_id();

	return 0;
}

So, only a programmer uses the coding() method.

Note that we included <typeinfo> in the example. The typeid operator returns a reference to a type_info object, where type_info is a class defined in the typeinfo header file.



ImsilOkJungHo4

Full List of C++ Tutorials