Cplus plus - Hoi-Jeon/Wiki GitHub Wiki

Upcasting and Downcasting

class Parent {
public:
  void sleep() {}
};

class Child: public Parent {
public:
  void gotoSchool(){}
};

int main( ) 
{ 
  Parent parent;
  Child child;

  // upcast - implicit type cast allowed
  Parent *pParent = &child; 

  // downcast - explicit type case required 
  Child *pChild =  (Child *) &parent;

  pParent -> sleep();
  pChild -> gotoSchool();
    
  return 0; 
}

Upcasting

Upcasting allows us to treat a derived type as though it were its base type. That's how we decouple ourselves from knowing about the exact type we are dealing with.

Downcasting

The opposite process, converting a base-class pointer (reference) to a derived-class pointer (reference) is called downcasting. Downcasting is not allowed without an explicit type cast. The reason for this restriction is that the is-a relationship is not, in most of the cases, symmetric. A derived class could add new data members, and the class member functions that used these data members wouldn't apply to the base class.

Ref: Upcasting and Downcasting

std::function and std::bind

As described in Callable, when invoking a pointer to non-static member function or pointer to non-static data member, the first argument has to be a reference or pointer (including, possibly, smart pointer such as std::shared_ptr and std::unique_ptr) to an object whose member will be accessed.

class MyClass {
private:
    //just shorthand to avoid long typing
    typedef std::function<void (float result)> TCallback;

    //this function takes long time
    void longRunningFunction(TCallback callback)
    {
        //do some long running task
        //...
        //callback to return result
        callback(result);
    }

    //this function gets called by longRunningFunction after its done
    void afterCompleteCallback(float result)
    {
        std::cout << result;
    }

public:
    int longRunningFunctionAsync()
    {
        //create callback - this equivalent of safe function pointer
        auto callback = std::bind(&MyClass::afterCompleteCallback, 
            this, std::placeholders::_1);

        //normally you want to start below function on seprate thread, 
        //but for illustration we will just do simple call
        longRunningFunction(callback);
    }
};

Ref: a good example from stackoverflow.com

Static cast example

// https://www.journaldev.com/37371/static-cast-in-c
#include <iostream>
 
using namespace std;
 
class BaseClass {
    public:
        int a, b;
        BaseClass(int val_a = 200, int val_b = 200) { 
        a = val_a; b = val_b; 
        }
        void print_obj() {
        cout<<"BaseClass Object: a = "<< a <<" , b = " <<b<< endl;
        }
        ~BaseClass() {
        }
};
 
// We can cast only if the inheritance is public
class DerivedClass : public BaseClass {
    public:
        int c;
        DerivedClass(int val_c = 100) { c = val_c; }
        void print_obj() {
            cout << "DerivedClass Object: a = " << a << " , b = " << b << " , c = " << c << endl; 
        }
        ~DerivedClass() {
        }
};
 
int main() {
    BaseClass* base_obj = new BaseClass(20, 25);
    DerivedClass* derived_obj = new DerivedClass(10);
 
    base_obj->print_obj();
    derived_obj->print_obj();
 
    // Cast downwards - Can do this only if you don't use Virtual Inheritance
    DerivedClass* base_to_derived = static_cast<DerivedClass*>(base_obj);
    cout << "After casting BaseClass object to DerivedClass, ";
    base_to_derived->print_obj();
 
    // Can cast upwards - Redundant
    BaseClass* derived_to_base = static_cast<BaseClass*>(derived_obj);
    // or "BaseClass* derived_to_base = (derived_obj);" gives the same results, that's why it is called "redundant"
    cout << "After casting DerivedClass object to BaseClass, ";
    derived_to_base->print_obj();
 
    // Free the pointers
    delete base_obj;
    delete derived_obj;
 
    return 0;
}

/*
BaseClass Object: a = 20 , b = 25
DerivedClass Object: a = 200 , b = 200 , c = 10
After casting BaseClass object to DerivedClass, DerivedClass Object: a = 20 , b = 25 , c = 0
After casting DerivedClass object to BaseClass, BaseClass Object: a = 200 , b = 200
*/

Dynamic cast example

How do we use the dynamic_cast?

void f(Parent* p) {
  Child *ptr = dynamic_cast<Child*>(p);
   if(ptr) { 
    // we can safely use ptr
  } 
}

In the code, if (ptr) is of the type Child or else derived directly or indirectly from the type Child, the dynamic_cast converts the pointer p to a pointer of type Child. Otherwise, the expression evaluates to 0, the null pointer.
In other words, we want to check if we can use the passed in pointer p before we do some operation on a child class object even though it's a pointer to base class.

The need for dynamic_cast generally arises because we want perform derived class operation on a derived class object, but we have only a pointer-or reference-to-base. -Scott Meyers
[Ref: upcasting and downcasting]https://www.bogotobogo.com/cplusplus/upcasting_downcasting.php

// https://www.journaldev.com/37371/static-cast-in-c
#include <iostream>
using namespace std;
 
class BaseClass {
    public:
        int a, b;
        BaseClass(int val_a = 200, int val_b = 200) { 
        a = val_a; b = val_b; 
        }
        virtual void print_obj() {
        cout<<"BaseClass Object: a = "<< a <<" , b = " <<b<< endl;
        }
        ~BaseClass() { }
};
 
class DerivedClass : public BaseClass {
    public:
        int c;
        DerivedClass(int val_c = 100) { c = val_c; }
        void print_obj() {
            cout << "DerivedClass Object: a = " << a << " , b = " << b << " , c = " << c << endl; 
        }
        ~DerivedClass() { }
};
 
int main() {
    BaseClass* base_obj = new BaseClass(20, 25);
    DerivedClass* derived_obj = new DerivedClass(10);    
    BaseClass* base_test = new DerivedClass(11);
 
    base_obj->print_obj();
    derived_obj->print_obj();
 
    // Downcasting - does not work, since **base_obj** is created from "new BaseClass"
    DerivedClass* base_to_derived = dynamic_cast<DerivedClass*>(base_obj);
    if (base_to_derived == nullptr)
        cout << "After casting BaseClass object to DerivedClass, Error\n";
    else {
        cout << "SHOULD NOT REACH HERE";
    }
    
    // Downcasting - works, since **base_test** is created from "new DerivedClass"
    DerivedClass* test = dynamic_cast<DerivedClass*>(base_test);
    if (test == nullptr)
        cout << "SHOULD NOT REACH HERE";
    else {
        cout << "After casting BaseClass object to DerivedClass, ";
        test->print_obj(); 
    }
 
    // Upcasting - redundant
    BaseClass* derived_to_base = dynamic_cast<BaseClass*> (derived_obj);
    cout << "After casting DerivedClass object to BaseClass, ";
    derived_to_base->print_obj();
 
    delete base_obj;
    delete derived_obj; 
    return 0;
}

/*
BaseClass Object: a = 20 , b = 25
DerivedClass Object: a = 200 , b = 200 , c = 10
After casting BaseClass object to DerivedClass, Error
After casting BaseClass object to DerivedClass, DerivedClass Object: a = 200 , b = 200 , c = 11
After casting DerivedClass object to BaseClass, DerivedClass Object: a = 200 , b = 200 , c = 10
*/
⚠️ **GitHub.com Fallback** ⚠️