C++ type cast

Type casting in C++ allows you to convert a variable from one type to another. There are several types of casts in C++: C-style casts, static_cast, dynamic_cast, reinterpret_cast, and const_cast. Each has its own specific use cases and advantages.

C-style Type Casting

C-style casting is the traditional method used in C. It can be done explicitly or implicitly:

int x = 10;
double y1 = x;         // Implicit casting
double y2 = (double)x; // Explicit casting
cout << "C-style cast: y1 = " << y1 << "; y2 = " << y2 << endl;

C++ Style Type Casting

C++ provides more specific casting operators for safer and clearer type conversions.

static_cast

static_cast is used to perform conversions between pointers to related classes, or for non-pointer implicit conversions. It does not perform runtime checks, so use it when you are sure of the types being casted.

int x = 10;
double y = static_cast(x);
cout << "static_cast: y = " << y << endl;

It can also convert pointers between related classes

Base* basePtr = new Derived1();
Derived1* derivedPtr1 = static_cast<Derived1*>(basePtr);
cout << "static_cast: correct behavior, ";
derivedPtr1->print();
delete basePtr;

Incorrect usage can lead to undefined behavior:

Base* basePtr = new Base();
Derived1* derivedPtr1 = static_cast<Derived1*>(basePtr);
cout << "static_cast: wrong behavior, ";
derivedPtr1->print();

With smart pointers:

shared_ptr basePtr = make_shared<Derived1>();
shared_ptr<Derived1> derivedPtr1 = static_pointer_cast<Derived1>(basePtr);
cout << "static_pointer_cast: correct behavior, ";
derivedPtr1->print();

shared_ptr<Base> wrongBasePtr = make_shared<Base>();
shared_ptr<Derived1> wrongDerivedPtr1 = static_pointer_cast<Derived1>(wrongBasePtr);
cout << "static_pointer_cast: wrong behavior, ";
wrongDerivedPtr1->print();

dynamic_cast

dynamic_cast is used for safe downcasting. It performs a runtime check to ensure the cast is valid and only works with polymorphic types (classes with virtual functions).

Base* basePtr = new Derived1();
Derived1* derivedPtr = dynamic_cast<Derived1*>(basePtr);
cout << "dynamic_cast: ";
if (derivedPtr) {
  derivedPtr->print();
} else {
  cout << "failed\n";
}
delete basePtr;

With smart pointers:

shared_ptr<Base> basePtr = make_shared<Derived1>();
shared_ptr<Derived1> derivedPtr = dynamic_pointer_cast<Derived1>(basePtr);
cout << "dynamic_pointer_cast: ";
if (derivedPtr) {
  derivedPtr->print();
} else {
  cout << "failed\n";
}

reinterpret_cast

reinterpret_cast is used to cast one type to a completely different type. Be cautious, as it can lead to undefined behavior if misused.

int x = 65;
          char* charPtr = reinterpret_cast<char*>(&x);
          cout << "reinterpret_cast int " << x << " to char " << *charPtr << endl;
          
          Derived1* derivedPtr1 = new Derived1();
          Derived2* derivedPtr2 = reinterpret_cast<Derived2*>(derivedPtr1);
          cout << "reinterpret_cast, Derived1 to Derived2: ";
          derivedPtr2->print();
          delete derivedPtr1;

With smart pointers:

shared_ptr<Base> basePtr = make_shared<Base>();
shared_ptr<Derived2> derivedPtr2 = reinterpret_pointer_cast<Derived2>(basePtr);
cout << "reinterpret_pointer_cast, Base to Derived2: ";
derivedPtr2->print();

const_cast

const_cast is used to add or remove the const qualifier from a variable or pointer.

const int x = 10;
int* ptr = const_cast<int*>(&x);
*ptr = 20; // Now you can modify x
cout << "const_cast: use with caution. With my compiler, x = " << x << "; but *(&x) = " << *(&x) << endl;

shared_ptr<const int> ptr1 = make_shared<const int>(10);
shared_ptr<int> ptr2 = const_pointer_cast<int>(ptr1);
*ptr2 = 20;
cout << "const_pointer_cast: *ptr1 = " << *ptr1 << endl;

Complete Example