Skip to content

AnaMac3/42-CPP_Module_06

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

42-CPP_Module_06

42 Common Core CPP Module 06

  • Casting
  • Scalar types
  • Serialization
  • Polimorfismoa and Run-Time Type Identification

Table of Contents

Casting

Casting means converting a value from one type to another.

Example:

    int a = 42;
    float b = static_cast<float>(a); //converts int to float

Types of Conversion

  • Implicit Conversion: Performed automatically by the compiler when assigning between compatible types.

        int i = 10;
        float f = i; //assigning an int to a float triggers an implicit conversion
    
  • Explicit Conversion: Occurs when the programmer explicitly specifies the type conversion.

        int i = 20;
        float f = (float)i / 10; //explicit cast using C-style casting
    

C++ Cast Operators:

Cast Type Purpose Check Type
static_cast Safe, compile-time conversion between compatible types Compile-time
dynamic_cast Used with polymorphic classes; checks type at runtime Runtime
reinterpret_cast Reinterprets raw memory between unrelated types Dangerous, no safety check
const_cast Adds or removes const or volatile qualifiers Compile-time

static_cast

Used for safe, compile-time conversions between compatible types (built-in or related classes).

    static_cast<type>(expression);

Example:

    int i = 20;
    float f = static_cast<float>(i); //convertis int to float
  • Pointer conversions between related types: "Related" means that the types share an inheritance relationship (base <-> derived). You can cast a pointer from a base class to a derived class and vice versa.

        class Base {};
        class Derived : public Base {};
    
        Base* base = new Derived();
        Derived *derived = static_cast<Derived*>(base); //valid: Base and Derived are related
    

❌ Example that does not work:

    int *ptrInt = NULL;
    float *ptrFloat = static_cast<float*>(ptrInt); //❌ error: types are unrelated

const_cast

Used to add or remove const or volatile qualifiers.

    const_cast<type>(expresion);

Example:

    void aFunction(int *a)
    {
            std::cout << *a << endl;
    }
    int main()
    {
            int a = 10;
            const int* i = &a;

            aFunction(const_cast<int*>(i); //remove const to match parameter type

            return (0);
    }

reinterpret_cast

Used for low-level reinterpreting of memory between unrelated types. It does not change the bit pattern - it just reinterprets the same memory as a different type.

    reinterpret_cast<type>(expression);

⚠️ Very dangerous: it performs no checks and does not change the underlying memory.

Example:

    struct	Data
    {
    	int	id;
    	std::string name;
    	double val;
    };

    int main()
    {
    
            Data data = {1, "first", 30.3}
            // "Serialize"
            uintptr_t ptr = reinterpret_cast<uintptr_t>(data);
            
            // "Deserialize"
            Data *ptr;
            ptr = reinterpret_cast<*Data>(ptr);
    }

Explanation:

  • uintptr_t is an unsigned int type capable of storing a pointer without losing information.
  • reinterpret_cast converts the pointer address into a numeric representation.
  • The conversion does not copy or transform the object, it just changes how the memory address is viewed.
  • This is not real serialization: it's just pointer reinterpretation.
  • When "deserializing", the integer is reinterpreted again as a pointer to the same memory address.

dynamic_cast

Used with polymorphic classes (those that have at least one virtual function). It checks the validity of a cast at runtime.
Use dynamic_cast when downcasting in polymorphic hierarchies. Never use it for unrelated or primitive types.

    dynamic_cast<type>(expression);

Example with pointers:

    class Base {};
    class Derived : public Base {};
    
    Base* base = new Derived();
    Derived *derived = dynamic_cast<Derived*>(base); //works, derived points to Base object

    Base* notBase = NULL;
    Derived* fail = dynamic_cast<Derived*>(notBase); //returns NULL safely

Key point: if the pointer expression does not point to an object of type <type>, dynamic_cast returns NULL.
This is safe and does not throw exceptions.

Example with references:

    Base b = new B
    try
    {
            A& d = dynamic_cast<A&>(b); //fails because b is not A
    }
    catch (const std::exception &e)
    {
            std::cout << e.what() << std::endl; //bad_cast
    }
  • If the cast fails:
    • Pointer version -> returns NULL
    • Reference version -> throws std::bad_cast

Serialization

Serialization is the process of converting an object into a form that can be stored or transmitted (e.g., into a file or over a network). The reverse process, deserialization, reconstructs the original object from that form.
In this module, you implement a single "serialization" mechanism using reinterpret_cast and uintptr_t to convert a pointer to a raw number and back.
This is not real serialization, just pointer-to-integer conversion.

More things

Non-instantiable class

In ex00, ScalarConverter is designed as a non-instantiable utility class.

  • Only the static method

       ScalarConverter::convert(std::string str);
    

    should be used.

  • To prevent instantiation: constructors, destructors, and copy operations are made private.

This ensures the class acts like a namespace and cannot be instantiated.
If it were an abstract base class, then:

  • We would use a virtual destructor (possibly pure virtual)
  • Derived classes would implement the required interface

static methods

A static method beongs to the class itself, not to any specific instance.

class MyClass
{
    public:
        static void staticMethod();
};

int main()
{ 
    MyClass::staticMethod(); //call withoyt creating an object
    return (0);
}

Characteristics:

  • No access to this
  • Can only access static attributes/methods
  • Only one shared instance of static members exists Use when:
  • The logic does not depend on object state
  • You want to group related functions inside a class
  • You want shared data acress all instances

find() and std::string::npos

  • std::string::npos is the return value used to indicate "not found".

  • Returned by any of the find() functions

        str::string myStr = "Hello world.";
        if (mySrt.find('Bye') == std::string::npos)
        {
            std::cout << "Not found" << std::endl;
        }
    

More info

c++ Casting