C++ Style TypeCasts

Discussion in 'C++' started by Trinity, Jan 1, 2012.

  1. Trinity

    Trinity New Member

    Joined:
    Nov 23, 2011
    Messages:
    28
    Likes Received:
    11
    Trophy Points:
    3
    Occupation:
    Software Engineer
    In C-Style typecasting we discussed about type casting and why is it required and how it is used in C programming. C++ has more types of typecasting and an entirely different way to use them.

    A point to be noted that C++ typecasting would be mostly described and used in context of classes, as an effect of C++ being object oriented.

    Types of C++ TypeCasting



    There are following four types of type casting in C++:

    Static-cast



    Static cast is generally said to be used for non-polymorphic types. That is, the casting is done on the basis of compile time details. Static cast is done for type casting a base class to derived class and vice versa.

    The Syntax:
    Code:
    1classObject = static_cast<class-1>2classObject;
    
    Here is the usage source code snippet:
    Code:
    class Base
    {
    public:
        int data;
        void multiplyData(int var)
        {
            cout<< "In base class method\n";
            data = (data * var);
        }
    };
    
    class Derived: public Base
    {
    public:
        Derived(Base b)
        {
            data = b.data;
        }
        void multiplyData(int var)
        {
            cout<< "In derived class method\n";
            data = (data * (var *2));
        }
    };
    
    int main()
    {
        Base base;
        base.data = 3;
        base.multiplyData(10);
        cout<<base.data<<endl;
        Derived d = static_cast<Base>(base);
        d.multiplyData(10);
        cout<<d.data<<endl;
        
        return 0;
    }
    
    In the above example, we are not using any run-time polymorphism. The typecasting statement;
    Code:
    Derived d = static_cast<Base>(base);
    
    Here, basically we want to create an object of the derived class out of the Base class, and hence the need of the type casting. Since, we know the type of the final object to be used (i.e. the class 'Derived' in our example) at the compile time itself, therefore we use static type cast here.

    However, we need to define a copy constructor to leverage the type cast, else it might not know, how and what base object data members to map to the derived object data members. If you donot specify the copy constructor and if the compiler gets confused, it might thrown an error:
    Code:
    error: conversion from ‘Base’ to non-scalar type ‘Derived’ requested
    
    The solution to the above error is defining a copy constructor.

    Anyhow, although you must have guessed, here is the output of the above example:
    Code:
    In base class method
    30
    In derived class method
    600
    
    Besides, you can use static cast to convert an enumeration to int or any expression to void in which case, the actual value is lost. But do take care, static cast is not very safe, and at times, things compile fine using any invalid type cast, and the actual problem might surface out during run time.

    Dynamic-cast



    Dynamic cast is used for polymorphic types, that is the where types are determined at the runtime. Very apt scenarios are in inheritence where a pointers are type casted dynamically to any class in the inheritence hierarchy.

    The syntax:
    Code:
    lvalue = dynamic_cast<class1>expression
    
    Dynamic type casting takes care of the cast type safety and hence, if dynamic casting fails, a NULL pointer is returned, and a valid object pointer is returned in case of success.

    An example to illustrate the usage is
    Code:
    class Base
    {
    public:
        int data;
        virtual void multiplyData(int var)
        {
            cout<< "In base class method\n";
            data = (data * var);
        }
    };
    
    class Derived: public Base
    {
    public:
        void multiplyData(int var)
        {
            cout<< "In derived class method\n";
            data = (data * (var *2));
        }
    };
    
    int main()
    {
        //A pointer pointing to an object of Derived class
        Derived *derived = new Derived();
        derived->data = 3;
        //do some operation
        derived->multiplyData(10);
        cout<<derived->data<<endl;
        
        // A pointer pointing to an object of Base class
        Base *b = new Base();
        b->data = 6;
        //do some operation
        b->multiplyData(10);
        cout<<b->data<<endl;
        
        //dynamic cast derived to Base class pointer
        b = dynamic_cast<Base*>(derived);
        if(b != NULL) // dynamic cast sucess
        {
            b->data = 12;
            //check what b contains now
            b->multiplyData(10);
            cout<<b->data<<endl;
            
            delete(derived);
        }
    }
    
    In this example, we are trying to typecast a Derived class pointer to Base class at runtime. By runtime, a Base class pointer is allowed to point to a Base class object as well as to a Derived class object by the compiler. Hence, it can be governed by the programmer at the runtime to change the object the pointer is pointing to.

    The output being:
    Code:
    In derived class method
    60
    In base class method
    60
    In derived class method
    240
    
    Please note, we've added a check after the dynamic cast on the resulting pointer 'b' if it's NULL or not. In case the dynamic cast is success, non-null desired pointer is returned. A null pointer is returned in case the dynamic cast fails. As for example, when we try to type cast a derived class pointer to a base class, which is invalid and hence results in a failure. The source code snippet is:
    Code:
    derived = dynamic_cast<Derived*>(b);//fails
    //derived contains NULL now
    

    Const-cast



    The const-cast is used to modify the contant-ness or volatility of any expression. It does not affect the data type of the variable or expression.

    The Syntax:
    Code:
    lvalue = const_cast<type>(expression);
    
    An example:
    Code:
    int cval = 10;
    const int* pval = &cval;
    int* vval = const_cast<int *>(pval);
    (*vval)++;
    cout<<"Modified val is "<<(*vval)<<endl;
    
    The output is
    Code:
    Modified val is 11
    
    Here, if we just try to assign the pointer 'vval' the same address as the constant pointer 'pval', we get a compile time error there itself. As in,
    Code:
    int cval = 10;
    const int* pval = &cval;
    int* vval pval;
    (*vval)++;
    cout<<"Modified val is "<<(*vval)<<endl;
    
    Compilation Error:
    Code:
    error: invalid conversion from ‘const int*’ to ‘int*
    
    Hence, this clearly explains the usage and the need for const_cast operator. Besides, const_cast operator can be used to convert a const object to a non-contant, a volatile variable to a non-volatile, a 'pointer to a constant' to a 'pointer to a modifiable value' and even a 'constant pointer' to a non-constant one.

    All these mentioned scenarios are possible visa versa as well.

    Re-interpret cast



    As the name suggests, re-interpret casts are used to type cast any type to any type. All conversions from a pointer type to any random unrelated pointer type is allowed, and hence making its to be used really carefully. This cast could be leading to unsafe type casts leading to loss of data or unexpected results.
    The Syntax:
    Code:
    lvalue = reinterpret_cast<type1>expression
    
    An Example:
    Code:
    class One
    {
    public:
        int data;
        One()
        {
            data = 1;
        }
    };
    
    class Two
    {
    public:
        int data;
        Two()
        {
            data = 2;
        }
    };
    
    int main()
    {
        One *one = new One();
        Two *two = new Two();
        cout<<"one: Data val is "<<one->data<<endl;
        cout<<"two: Data val is "<<two->data<<endl;
        delete two;
        // convert one of type Class Two 
        two = reinterpret_cast<Two*>(one);
        if (two != NULL)
        {
            //check data of the object pointed by 'two'.
            cout<<"two After reinterpret cast : Data val is "<<two->data<<endl;
        }
        
        return 0;
    }
    
    Here, we have two pretty similar in layout classes - 'One' and 'Two', but totally unrelated with each other. Using pointers, we created objects of both of them and checked the data each contains.

    Deleting the object pointed by 'two' pointer, which was pointing to an object of class 'Two', we reinterpret cast 'one' pointer which is of type class 'One' pointer to type class 'Two' pointer and assign it to pointer variable 'two'.

    Now checking, the data what 'two' points to, lets observe the output:
    Code:
    one: Data val is 1
    two: Data val is 2
    two After reinterpret cast : Data val is 1
    
    Oh wow, now 'two' pointer variable points to the object containing the data of 'One'.

    However, reiterating, since reinterpret castings are for totally unrelated data types, the programmer is solely responsible for its usage. There might be unexpected results in case of invalid usage. Hence, one should be very sure prior to using reinterpret cast.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice