Type Casting
Info
This note was written with the assistance of ChatGPT.
In C++, static_cast
, dynamic_cast
, reinterpret_cast
and const_cast
serve different purposes for type conversion.
Cast Types
static_cast
static_cast
is used for conversions between types where the compiler can check the validity of the conversion at compile time. It is used for standard conversions of numeric types, such as converting an int
to a float
, and for converting between pointer types when there is a clear inheritance relationship.
Use cases:
- Converting numerical types (e.g.,
int
todouble
) - Converting enums to integers or vice versa
- Upcasting: converting a derived class pointer or reference to a base class pointer or reference
- Note: downcasting can also be performed with static_cast, which avoids the cost of the runtime check, but it's only safe if the program can guarantee (through some other logic) that the object pointed to by expression is definitely derived
Example:
int i = 10;
double d = static_cast<double>(i); // Convert int to double
class Base {};
class Derived : public Base {};
Derived* derived = new Derived();
Base* base = static_cast<Base*>(derived); // Upcasting
dynamic_cast
dynamic_cast
is used for safe downcasting in class hierarchies, i.e., converting a base class pointer or reference to a derived class pointer or reference. It checks the type at runtime and returns nullptr
(for pointers) or throws std::bad_cast
(for references) if the conversion is not possible.
Use cases:
- Downcasting in class hierarchies with polymorphic types (i.e., classes with virtual functions)
- Checking type at runtime in a class hierarchy
- Note: dynamic_cast may be used for upcasting, but unnecessary
Example:
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base); // Downcasting
if (derived) {
// Successfully downcasted
}
reinterpret_cast
reinterpret_cast
is used for low-level casts that yield implementation-dependent results. It converts any pointer type to any other pointer type, even if the types are unrelated, without any runtime type check.
Use cases:
- Converting pointers to and from integer types for low-level manipulation
- Treating a block of memory as an array of a different type
- Function pointer type conversions
Example:
long p = 12345678;
char* cp = reinterpret_cast<char*>(&p); // Treat the long int as an array of chars
void (*funcPtr)(int);
void* ptr = reinterpret_cast<void*>(funcPtr); // Convert function pointer to void pointer
const_cast
const_cast
is used to add or remove the const qualifier from a variable, allowing for temporary changes in constness.
Use Cases:
- Modifying a previously declared const variable
- Passing const objects to functions that require non-const parameters
Example:
Each of these casts serves a specific purpose, and choosing the right one depends on the context and the level of safety and type checking required. static_cast
is the safest and most commonly used for general type conversions. dynamic_cast
is more specialized for safe downcasting in polymorphic class hierarchies. reinterpret_cast
is the least safe and should be used sparingly, as it essentially allows treating any pointer as any other type of pointer.
Dynamic Casting Safety
dynamic_cast
can fail under certain conditions, typically when it's used for downcasting or sidecasting in class hierarchies. Here are the situations where dynamic_cast
will not succeed:
-
Type is not polymorphic: If the type you're casting from does not have at least one virtual function, then
dynamic_cast
cannot be used. It relies on runtime type information (RTTI) to check the object's type at runtime, which is available only for polymorphic types. -
Invalid downcast or sidecast:
dynamic_cast
will fail if you attempt to cast to a type that is not the actual type of the object or a derived type thereof. For example, if you try to downcast a base class pointer to a derived class pointer, but the actual object is not of that derived class, the cast will fail. -
Casting away constness improperly: While
dynamic_cast
can be used to cast between types within an inheritance hierarchy, it cannot change the constness of the object being cast. If you need to cast away constness,const_cast
must be used in conjunction. -
Cross-casting in multiple inheritance incorrectly: In cases of multiple inheritance, if you attempt to cast from one base class to another where neither is a base of the other (sidecasting), and the object is not actually an instance of the target class,
dynamic_cast
will fail.
Example
-
Polymorphic Base Required:
-
Invalid Downcast:
-
Improper Constness Casting:
To ensure dynamic_cast
succeeds, you must cast between compatible types within a polymorphic class hierarchy and adhere to the constness rules. When dynamic_cast
fails during pointer casting, it returns nullptr
, and when it fails during reference casting, it throws a std::bad_cast
exception.