Type Casts Notes
const_cast
Typically used to cast away the constness of objects. It is the only C++ style that can do this.
const int *a = new int(5);
cout << "a = " << *a << endl; // return 5
int *b = const_cast<int*>(a);
cout << "b = " << *b << endl; // return 5
*b = 9;
cout << "a = " << *a << endl; // return 9
cout << "b = " << *b << endl; // return 9
const int c = 10;
const_cast<int&>(c) = 11;
reinterpret_cast
Converts any pointer type to any other pointer type, even of unrelated classes. The operation result is a simple binary copy of the value from one pointer to the other.
Intended for low-level casts that yield implementation-dependent and it would not be portable.
Let’s consider an example:
struct Data {
char a;
char b;
};
int value = 261;
Data* pdata = reinterpret_cast<Data*> (&value);
cout << "a = " << pdata->a << ", b = " << pdata->b << endl; //returns a = 5, b = 1
value = 261 = 0000 0000 0000 0000 0000 0001 0000 0101 = 4bytes (int)
a = 5 = 0000 0101 = 1byte (char)
b = 1 = 0000 0001 = 1byte (char)
So reinterpret_cast
just read 1st and 2nd bytes of value
and wrote it into a
and b
. It treats variable value
of type int
as a variable of type struct Data
.
Another way of using reinterpret_cast
is to compare pointers.
Here is an example:
class A {};
class B {};
class C : public A, public B {};
C c;
A *a = &c; // implicit upcasting
B *b = &c; // implicit upcasting
// compare pointers
bool bool0 = a == &c;
bool bool1 = reinterpret_cast<char*>(a) == reinterpret_cast<char*>(&c);
bool bool2 = b == &c;
bool bool3 = reinterpret_cast<char*>(b) == reinterpret_cast<char*>(&c);
bool bool4 = reinterpret_cast<char*>(a) == reinterpret_cast<char*>(b);
std::cout << bool0 << bool1 << bool2 << bool3 << bool4 << std::endl; // 11100
cout << "Adress of c = " << &c << '\n' << "a = " << a << '\n' << "b = " << b << endl;
a == &c == true
- c
is implicitly casted to A
class, so a
and casted c
are identical.
reinterpret_cast<char*>(a) == reinterpret_cast<char*>(&c) == true
- treats a
and &c
as pointers to char
, the 1st byte is the same in both pointers.
b == &c == true
- c
is implicitly casted to B
class, so b
and casted c
are identical.
reinterpret_cast<char*>(b) == reinterpret_cast<char*>(&c) == false
- after upcasting b
kind of points at part of c
which has offset of sizeof(A)
.
reinterpret_cast<char*>(a) == reinterpret_cast<char*>(b) == false
- after upcasting a
points at the beginning of the c
and b
at part of c
which has offset of sizeof(A)
.
Output of the last string is:
Adress of c = 000000E77974F664
a = 000000E77974F664
b = 000000E77974F665
static_cast
Valid only for related objects(base, derived). Can be used for upcasting (child->base) for objects/pointers. Downcasting for objects won’t compile as it is incorrect, but for pointers it will but child’s members will contain garbage.
class Base {
public:
int a;
Base() { a = 7; }
};
class Derived : public Base {
public:
int b;
Derived() :Base() { b = 8; }
};
class UnrelatedClass {};
int main(){
Base base;
Derived derived;
// valid upcast with pointers
Base *pBase = static_cast<Base *>(&derived);
cout << "pBase->a = " << pBase->a << endl;
//cout << "pBase->b = " << pBase->b << endl;
// valid downcast with pointers
Derived *pDerived = static_cast<Derived *> (&base);
cout << "pDerived->a = " << pDerived->a << endl; // output 7
cout << "pDerived->b = " << pDerived->b << endl; // output some garbage, Base doesn't contain b
// invalid downcast - won't compile
Derived derived2 = static_cast<Derived> (base);
// invalid, between unrelated classes
//UnrelatedClass *pUnrelated= static_cast<UnrelatedClass *> (&derived);
return 0;
}
dynamic_cast
This kind of cast is used to perform safe downcasting, i.e., to determine whether an object is of a particular type in an inheritance hierarchy. It is the only cast that may have a significant runtime cost.
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.
class Base {
virtual void vf(){}
};
class Derived : public Base { };
int main()
{
Base *pbd = new Derived;
Base *pb = new Base;
Derived *pd;
pd = dynamic_cast<Derived*>(pbd); // correct, pbd points at an object of type Derived
pd = dynamic_cast<Derived*>(pb); // invalid, return nullptr
Base b;
Derived d;
Base *pb = dynamic_cast<Base*>(&d); // correct upcasting
Derived *pd = dynamic_cast<Derived*>(&b); // invalid downcasting, return nullptr
return 0;
}
A use case example:
#include <iostream>
#include <string>
using namespace std;
class Window
{
public:
Window(){}
Window(const string s):name(s) {};
virtual ~Window() {};
void getName() { cout << name << endl;};
private:
string name;
};
class ScrollWindow : public Window
{
public:
ScrollWindow(string s) : Window(s) {};
~ScrollWindow() {};
void scroll() { cout << "scroll()" << endl;};
};
void DoSomething(Window *w)
{
w->getName();
// w->scroll(); // class "Window" has no member scroll
// check if the pointer is pointing to a scroll window
ScrollWindow *sw = dynamic_cast<ScrollWindow*>(w);
// if not null, it's a scroll window object
if(sw) sw->scroll();
}
int main()
{
Window *w = new Window("plain window");
ScrollWindow *sw = new ScrollWindow("scroll window");
DoSomething(w);
DoSomething(sw);
return 0;
}
DoSomething(Window* w)
is passed down Window
pointer. It calls scroll()
method which is only available from Scroll
object. So, in this case, we need to check if the object is the Scroll
type or not before the call to the scroll()
method.
Example (static_cast/dynamic_cast):
class Dog {
public:
virtual ~Dog() {};
};
class YellowDog : public Dog {
public:
int a;
void bark() { std::cout << "Woof\n"; }
};
Dog *dog = new YellowDog();
YellowDog *dydog = dynamic_cast<YellowDog*>(dog);
YellowDog *sydog = static_cast<YellowDog*>(dog);
dydog->bark();
sydog->bark();
In both cases the result is Woof
, because bark() is treated as a static function.
Change bark()
to void bark() { std::cout << "Woof\n" << a; }
then bark()
won’t be static anymore and sydog->bark()
prints Woof + some garbage
because Dog
doesn’t have the field int a
and dydog->bark()
will trow an exception because of the access through nullptr
.
It’s better not to use static_cast
here and instead use dynamic_cast
but first check that upcasting performed correctly:
YellowDog *dydog = dynamic_cast<YellowDog*>(dog);
if(dydog)
dydog->bark();
A better way is to use dynamic polymorphism:
class Dog {
public:
virtual void bark() {}
virtual ~Dog() {}
};
Dog *dog = new YellowDog();
dog->bark();