In C++, each type of cast serves a specific purpose. Here’s a clear guide on when to use static_cast
, dynamic_cast
, const_cast
, and reinterpret_cast
:
1. static_cast
- Purpose: Convert between related types (e.g., numeric types, pointers in a class hierarchy).
- Use Cases:
- Numeric Conversions:
int
todouble
,enum
toint
, etc. - Pointer/Reference Upcasting: Convert a derived class pointer to a base class pointer (safe, no runtime check).
- Explicit Type Conversions: When implicit conversion isn’t allowed (e.g.,
void*
toint*
). - Avoid Undefined Behavior: Safer than C-style casts but no runtime checks.
- Example:
double d = 3.14;
int i = static_cast<int>(d); // i = 3
Base* base_ptr = static_cast<Base*>(derived_ptr); // Safe upcast
2. dynamic_cast
- Purpose: Safely handle polymorphism (downcasting in class hierarchies).
- Use Cases:
- Downcasting: Convert a base class pointer/reference to a derived class pointer/reference.
- Runtime Type Check: Verifies validity (returns
nullptr
for pointers, throwsstd::bad_cast
for invalid references). - Requires: At least one
virtual
function in the base class (RTTI enabled). - Example:
Base* base_ptr = new Derived();
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
if (derived_ptr) { // Check if cast succeeded
// Use derived_ptr...
}
3. const_cast
- Purpose: Add or remove
const
/volatile
qualifiers. - Use Cases:
- Legacy Code: Call non-
const
functions fromconst
objects (e.g., outdated APIs). - Modify
const
Data: Only if the original object is notconst
(undefined behavior otherwise). - Avoid: Modifying originally
const
data. - Example:
const std::string str = "Hello";
// Remove const to call a non-const function (use with caution!)
auto& non_const_str = const_cast<std::string&>(str);
non_const_str.clear(); // UB if `str` was originally const!
4. reinterpret_cast
- Purpose: Low-level type punning (reinterpret memory as another type).
- Use Cases:
- Pointer ↔ Integer: Convert pointers to integers (e.g., for serialization).
- Unrelated Pointer Types: Treat memory as a different type (e.g., network packet parsing).
- System-Level Code: Hardware registers, driver development.
- Risks: Platform-dependent, unsafe, and bypasses type safety.
- Example:
int* ptr = new int(42);
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr); // Pointer to integer
// Treat memory as a different type (dangerous!)
char* bytes = reinterpret_cast<char*>(ptr);
Summary Table
Cast | Use Case | Safety | Runtime Check |
---|---|---|---|
static_cast | Related types, numeric, upcasting | Safe if correct | No |
dynamic_cast | Polymorphic downcasting | Safe | Yes |
const_cast | Modify const /volatile qualifiers | Risky | No |
reinterpret_cast | Low-level type punning, unrelated pointers | Unsafe | No |
Best Practices
- Prefer
static_cast
for most conversions (avoids C-style casts). - Use
dynamic_cast
for safe downcasting in polymorphic hierarchies. - Avoid
const_cast
unless interfacing with legacy code. - Minimize
reinterpret_cast
(it bypasses type safety).
By choosing the appropriate cast, you write safer, clearer, and more maintainable C++ code.