Casting is one of the most troublesome operations in C/C++ as when you use casts you are directing the compiler to trust you and to forget about type checking. Casts should be used as infrequently as possible and only when there is no alternative. We have two main terms for casting, upcasting and downcasting. Upcasting is casting from a derived class to one of its base classes and downcasting is casting from a base class to one of its derived classes. Remember that the inheritance tree branches down with the base class at the top. We also have the lesser known cross-casting for casting a class to a sibling class.
C++ introduces new explicit casts that identify the rationale behind the cast, clearly identifies that a cast is taking place and confirms type-safe conversions. These casts are:
static_cast
for well-behaved casts such as automatic conversions, narrowing
conversions (e.g. a float to int), a conversion from void*
, and
safe downcasts (moving down the inheritance hierarchy) for non-polymorphic classes. If the base class is a virtual
base class then you must use a dynamic_cast
dynamic_cast
is used for type-safe downcasting. The format of a dynamic cast is
dynamic_cast <type>(expression)
and requires that the expression is a pointer if the type
is a pointer type. If a dynamic_cast
fails then a null
pointer is returned.
The dynamic_cast
is actually safer than the static_cast
as it performs
a run-time check, to check for ambigious casts (in the case of multiple-inheritance).
const_cast
is used for casting away const
or volatile
.
reinterpret_cast
is the most dangerous cast. It allows us to convert an object into any
other object for the purpose of modifying that object - often for low-level "bit-twiddling" as it is known.
Here is an example of all four casts:
1 2 // Start reading from main() function to help make it easier to understand 3 #include <iostream> 4 using namespace std; 5 6 // Demo classes with a virtual base class 7 class Account { 8 public: 9 float balance; // for demonstration only! 10 virtual ~Account(){} 11 }; 12 class Current: public Account {}; 13 class Test { public: float x, y;}; 14 15 // Demo Function 16 void someFunction(int& c) 17 { 18 c++; // c can be modified 19 cout << "c has value " << c << endl; //will output 11 20 } 21 22 int main() 23 { 24 float f = 200.6f; 25 // narrowing conversion, but we have notified the compiler 26 int x = static_cast<int>(f); 27 cout << x << endl; 28 29 Account *a = new Current; //upcast - cast derived to base 30 //type-safe downcast - cast base to derived 31 Current *c = dynamic_cast<Current*>(a); 32 33 const int i = 10; // note i is const 34 //someFunction(i); // is an error as someFunction 35 // could modify the value of i 36 someFunction(*const_cast<int*>(&i)); 37 // Allow i be passed to the function but it will still remain at 10. 38 cout << "i has value " << i << endl; // will still output 10 39 40 a = new Account; 41 a->balance = 1000; 42 //convert account address to long 43 long addr = reinterpret_cast<long>(a); 44 45 // safe to convert long address into an Account 46 Account* b = reinterpret_cast<Account*>(addr); 47 cout << "The balance of b is " << b->balance << endl; 48 49 // could convert to any class regardless of 50 // inheritance - ok this time! (not safe) 51 Current* cur = reinterpret_cast<Current*>(addr); 52 cout << "The balance of cur is " << cur->balance << endl; 53 54 // works, but not definitely not safe this time! 55 Test* test = reinterpret_cast<Test*>(addr); 56 cout << "The value of the float x is " << test->x << 57 " and float y is " << test->y << endl; 58 } 59The output of this code is:
C:\My Documents\My Teaching\OOP Notes\EE553_Output\cpp>casting 200 c has value 11 i has value 10 The balance of b is 1000 The balance of cur is 1000 The value of the float x is 1000 and float y is 5.98875e-39
© 2006 Dr. Derek Molloy (DCU).