# C++ Casts
## Basic concepts
### What's scalar types?
- A scalar type is a basic data type that represents a single value (not composed of other values). It contrasts with more complex types like arrays, structs, or objects.
- Typically incl:
- Int
- Float
- Double
- Char
- Bool
### What's type casting?
- Type casting is the process of converting a value from one data type to another — like turning a float into an int.
- It’s called “casting” because you're telling the compiler:
> 🧙 “Hey, treat this value as this type, not what it originally was.”
- Two types of casting
- Implicity casting (auto)
- The compiler does this for you when it's safe
```c++
int x = 5;
double y = x;
```
x is auto converted to 5.0
- Explicit casting (manual)
- Explicitly telling the compiler to cast pi from double to int
```c++
double pi = 3.14;
int truncated = (int)pi;
```
- Danger of casting
- Can lose data: eg. from float to int
- Can cause undefined behavior: casting incompatible pointers/ references
### What's ```static_cast``` vs ```dynamic_cast```?
#### static_cast: compile time conversion
✅ What it does:
- Performs explicit, safe conversions known at compile-time.
🔧 Use cases:
- Convert between numeric types (e.g., int to float)
- Convert a pointer or reference in a class hierarchy (e.g., Derived* to Base*)
- Convert void* to another pointer type (if you're sure it’s safe)
🔐 Safety:
- Checked at compile time
- No runtime checks — if you do something unsafe (like downcasting to the wrong type), it will compile, but could lead to undefined behavior
Example:
```c++
class Animal {};
class Dog : public Animal {};
Dog* d = new Dog();
Animal* a = static_cast<Animal*>(d); // ✅ Upcasting: safe
```
#### dynamic_cast: runtime conversion
✅ What it does:
- Used for safe downcasting in an inheritance hierarchy — from a base class to a derived class.
🔧 Use cases:
- Cast Base* to Derived* only if the object is actually of Derived type
- Requires at least one virtual function in the base class (i.e., RTTI enabled)
🔐 Safety:
- Checks at runtime whether the cast is valid
- If the object isn’t of the correct derived type:
- For pointers → returns ```nullptr```
- For references → throws ```std::bad_cast```
Example:
```c++
class Animal {
public:
virtual ~Animal() {} // Required for dynamic_cast to work
};
class Dog : public Animal {};
class Cat : public Animal {};
Animal* a = new Dog();
Dog* d = dynamic_cast<Dog*>(a); // ✅ succeeds
Cat* c = dynamic_cast<Cat*>(a); // ❌ returns nullptr
```
#### When to use static or dynamic cast
- Use static_cast when
- You know the conversion is safe
- Want speed and simplicity
- Use dynamic_cast when
- You are downcasting and need runtime safety
- Not sure of the actual type
### What's ```reinterpret_cast``` and ```const_cast```
#### reinterpret_cast
🔄 What is reinterpret_cast?
- reinterpret_cast is a C++ casting operator that performs a low-level, unsafe conversion between unrelated types, typically between:
- Different pointer types
- Pointers and integers
- References of unrelated types
- Unlike static_cast, which checks types at compile time and is relatively safe, reinterpret_cast says:
>"Treat the raw bits of this object as if they were something else."
🧠 What it does under the hood:
- It does not convert values
- It reinterprets the binary representation of a value or pointer
- So the bit pattern stays the same, but the type changes
✅ When to use it?
Common safe use cases:
- Pointer <-> Integer conversions (e.g., uintptr_t)
- Casting between two pointer types when you know it’s valid (e.g., void* to char*)
- Low-level systems or serialization code
⚠️ When to avoid it?
- Casting between unrelated classes or types that don’t share memory layout — leads to undefined behavior
- To bypass type system protections — it’s dangerous and discouraged unless necessary
🔁 Examples (Conceptual)
- Pointer to int
```c++
Data* ptr = new Data();
uintptr_t raw = reinterpret_cast<uintptr_t>(ptr); // serialize
```
Give the numeric memory address of this ptr
- Int back to pointer
```c++
Data* ptr2 = reinterpret_cast<Data*>(raw); //deserialize
```
> Take this int and treat it like a ptr to Data
#### const_cast
🔹 What is const_cast?
- const_cast is used to add or remove const (or volatile) qualifiers from a variable's type.
- You typically use it to:
- Remove const so that a value can be modified (⚠️ risky!)
- Call a function that expects a non-const pointer/reference, but you only have a const one
🧠 What it does:
- It tells the compiler:
> “I promise I know what I’m doing — please treat this const value as if it were non-const.”
- But if the original value really is const (i.e., stored in read-only memory), modifying it causes undefined behavior.
✅ Common Use Case (Safe)
Imagine you have a function that doesn’t modify its argument, but isn’t marked const:
```c++
void legacyPrint(char *str);
const char* msg = "Hello";
legacyPrint(const_cast<char*>(msg));
```
Here, you're calling a function that expects char*, but you only have a const char*. As long as you don’t modify the data inside the function, it's safe.
❌ Dangerous Use Case (undefined behavior)
```c++
const int x = 42;
int* ptr = const_cast<int*>(&x);
*ptr = 99; // ❌ undefined behavior if x is truly const
```
You're trying to change a value marked const. Even though the compiler lets you do it, this is not allowed and may crash or behave unpredictably.
🧠 Rule of Thumb
- **Only use const_cast when you are absolutely sure the object wasn’t originally const**.
- It’s meant to help interface with poorly designed APIs — not to override language safety.
#### Summary of all four casting operators
| Cast | Purpose | Runtime Checked? | Typical Use Case |
| ------------------ | --------------------------------------------- | ---------------- | ----------------------------------- |
| `static_cast` | Compile-time safe conversions | ❌ No | Upcasting, scalar conversions |
| `dynamic_cast` | Safe downcasting in class hierarchies | ✅ Yes | Polymorphic downcasting |
| `const_cast` | Add or remove `const` / `volatile` qualifiers | ❌ No | Remove `const` from pointers |
| `reinterpret_cast` | Low-level, unsafe reinterpretation of bits | ❌ No | Raw memory tricks, casting pointers |
### What's serialization
Normally, serialization means converting an object into a format that can be stored/transmitted and then reconstructed.
In ex01, it's used more literally and simply: take a pointer to a struct, convert it into a raw integer, then back into the original pointer type. It's more about **low-level representation than saving data**.
### The ```uintptr_t``` type
Unsigned int type from <cstdint> that is guaranteed to be able to hold a pointer.
This makes it safe to convert pointers to int and back without losing info.
## Ex00 Conversion of scalar types
### Project goal
Convert an input string, into char, int, float, and double
### Process
1. Parse the input
2. Try converting the input to each type (char, int, float, double)
3. Handle edge cases (nan, +inf, non displayable chars etc.)
4. Print the result for all four types, handling impossible or invalid conversions
### Relevant functions & techniques
#### Conversion functions
- ```std::stoi()``` - string to int
- ```std::stof()``` - string to float
- ```std::stod()``` - string to double
#### Exception handling
```c++
try {
int i = std::stoi(input);
} catch (std::exception& e) {
// handle conversion error
}
```
#### Type casting
To convert between types
```c++
float f = static_cast<float>(int);
int i = static_cast<int>(char);
```
#### Character checks
To verify if a ```char``` is displayable
- ```std::isprint(char)``` - check if a char is printable
#### Detect special values
Manually handle special float/double values like:
- nan, nanf
- +inf, -inf, +inff, -inff
Need to use: ```std::isnan()```, ```std::isinf()``` in the cmath lib
#### String manipulation
To detect if a string ends with "f" for float literals
- ```str.back() == 'f'```
- ```str.find('.')``` to check for decimal point
## Ex01 Serialization
### Project goal
Implement a basic serializer/ deserializer that converts a pointer to an int, then converts it back.
Key focuses:
- Pointer manipulation
- Type casting (reinterpret_cast)
- Data representation in memory
## Ex02 Identify real type
### Project goal
Identify the actual derived type of an object through a base class pointer or reference at runtime.
Key focuses:
- Inheritance
- Virtual functions
- dynamic_cast
- Runtime type identification
### Setup conceptually
You have:
- A base class called Base
- Three derived classes: A, B, and C, all inherit from Base
Now imagine:
- You randomly create an object of type A, B, or C
- You only store a pointer or reference to Base
- Your job is to identify the real type of that object at runtime
### Key function: ```dynamic_cast```
- Work only with polymorphic classes
- Allow to downcast from Base* to Derived*
- Return nullptr if the cast is invalid (when using ptr)
- Throw an exception if it fails on a reference