Broad Network


Type Casting Operators in C++

C++ Operators – Part 6

Forward: In this part of the series, we look at C++ type casting operators.

By: Chrysanthus Date Published: 23 Aug 2012

Introduction

This is part 6 of my series, C++ Operators. Casting means changing the object type of an operand to another; you need operators to do that. In this part of the series, we look at C++ type casting operators. We first look at casting with fundamental types. After that we look at casting with instantiated objects. Instantiated objects are objects created from classes.

Note: If you cannot see the code or if you think anything is missing (broken link, image absent), just contact me at forchatrans@yahoo.com. That is, contact me for the slightest problem you have about what you are reading.

Implicit Conversion
You can convert one type of fundamental object to another type implicitly. You do this using the simple assignment operator. The following code illustrates this:

#include <iostream>
using namespace std;

int main()
    {
        float hisFlt = 23.67;
        int myInt = hisFlt;

        cout << myInt;

        return 0;
    }

The first statement in main initializes a float object (hisFlt). The value of this float object has a decimal part. The second statement in main assigns the float object to an int. This is implicit casting, because no casting operator has been used. The myInt object now holds an int whose value should be the same as the value of hisFlt. You still have two objects, each with its value and after the conversion, the values in the two objects should be the same.

Well, with implicit conversion (type casting) there may be loss of precision. The value of myInt is actually, 23 without the decimal part; an integer can never have a decimal part.

You can cast fundamental objects using implicit conversion (simple assignment), but you may lose precision depending on the nature of the operands involved on either side of the assignment operator.

Implicit conversion has the disadvantage that you are still using the simple assignment operator, and in this case the meaning of the simple assignment operator is modified. To avoid this disadvantage you can use explicit conversion (see below).

Explicit Conversion
Explicit conversion does the same thing that implicit conversion does but this time you use parentheses to indicate that the casting is explicit. With explicit conversion you still use the simple assignment operator, but its meaning is not modified; it does the simple assignment. After the assignment operator, the second operand is preceded by the new object type envisaged. On the right side of the assignment operator you can put the new object type in parentheses or you put the second operand in parentheses. The following code shows the case where the new object type is in parentheses:

#include <iostream>
using namespace std;

int main()
    {
        float hisFlt = 23.67;
        int myInt = (int) hisFlt;

        cout << myInt;

        return 0;
    }

Note the second statement in main. The following code shows the case where the second operand is in parentheses:

#include <iostream>
using namespace std;

int main()
    {
        float hisFlt = 23.67;
        int myInt = int (hisFlt);

        cout << myInt;

        return 0;
    }

Note the second statement in main. With explicit conversion, you still have loss of precision depending on the nature of the operands on either side of the assignment operator. The output of the above two code samples is 23 without the decimal part. The explicit conversion works just like implicit conversion, but this time parentheses are used to indicate that casting is taking place, and the meaning of the assignment operator is not modified. Always remember that both implicit and explicit conversions can have loss of precision in the same way.

When you want to cast objects of fundamental types use implicit or explicit conversion casting. What about instantiated objects? For this, use the casting operators given below. You need basic knowledge in C++ Object Oriented programming to understand the rest of the article. If you do not have that knowledge, then read the series I wrote titled, Object Oriented Programming in C++. To reach the series, type the title and my name Chrys in the Search Box of this page and click Search.

dynamic-cast
The dynamic-cast operator is an operator that is used to cast a derived object to a base object. This operator works with pointers (and references) to both objects. The following code shows how a derived object is cast into a base object.

#include <iostream>
using namespace std;

class TheBase
    {
        public:
        void bMthd()
            {
                cout<<"I am of the base";
            }
     };

class TheDerived: public TheBase
    {
        public:
        void dMthd()
            {
                cout<<"I am of the derived";
            }
    };

TheDerived dObj;
TheBase *bObjPtr;

int main()
    {
        bObjPtr = dynamic_cast<TheBase*>(&dObj);

        return 0;
    }

In the code, you have the base class described. Then you have the derived class described. After that you have a declaration statement that instantiates an object for the derived class. Next you have another declaration statement that instantiates an object for the base class but with a pointer. At this point, you have a derived class object identified by an ordinary identifier (without the * or & operator). You also have a base class object pointed to by a pointer.

In the main function the dynamic_cast operator is used. It casts the derived instantiated object to the base instantiated object. The base object is now a converted form of the derived object and no longer what is was.

The dynamic_cast operator uses the pair of angle brackets and parentheses. In the parentheses you have the address of the source object (derived object). In the angle brackets you have the pointer type specifier of the target object (base object). The return value received at the left side of the simple assignment operator, is the pointer (address) to the converted object. In the dynamic_cast statement above, bObjPtr would now be pointing to a copy of the derived object converted, and no longer whatever it was pointing to. At the end there are two objects, as they were at the beginning. The difference now is that bObjPtr is no longer pointing to whatever it was pointing to before. It is now pointing to a copy of the derived object converted.

In simple terms the syntax for the dynamic_cast operator is:

        baseObjPtr = dynamic_cast<baseClassName*>(&IDOfDerivedObj);

You can now use the converted derived object (base object pointer) as you would use the base object. The following code illustrates this:

#include <iostream>
using namespace std;

class TheBase
    {
        public:
        void bMthd()
            {
                cout<<"I am of the base";
            }
     };

class TheDerived: public TheBase
    {
        public:
        void dMthd()
            {
                cout<<"I am of the derived";
            }
    };

TheDerived dObj;
TheBase *bObjPtr;

int main()
    {
        bObjPtr = dynamic_cast<TheBase*>(&dObj);

        bObjPtr->bMthd();

        return 0;
    }

Question: With the conversion of fundamental types there can be loss of precision. Is there similarly, a loss of member with the dynamic_cast operator that converts derived object to base object? Yes there is.

Each of the above two classes has its own method. The base class method is bMthd() but the derived class method is dMthd(). The following statement will not work:

        bObjPtr->dMthd();

This confirms the loss of members from derived to base class as the conversion takes place.

static_cast
The static_cast operator can be used to convert a base class object to a derived class object. It works using pointers (and references) to the base and derived objects. Know that the static_cast operator is not as reliable as the dynamic-cast operator. Its syntax is the same as with dynamic-cast, but this time, the positions of the pointers (references) are inter-changed, since you are converting from base to derived and not derived to base. The following code illustrates the use of the static_cast operator:

#include <iostream>
using namespace std;

class TheBase
    {
        public:
        void bMthd()
            {
                cout<<"I am of the base";
            }
     };

class TheDerived: public TheBase
    {
        public:
        void dMthd()
            {
                cout<<"I am of the derived";
            }
    };

TheBase bObj;
TheDerived *dObjPtr;

int main()
    {
        dObjPtr = static_cast<TheDerived*>(&bObj);

        dObjPtr->dMthd();

        return 0;
    }

In simple terms, the syntax of the static_cast operator to convert a base object to a derived object is,

        derivedObjPtr = static_cast<derivedClassName*>(&IDOfBaseObj);

Some good news here: with the static_cast operator and in the conversion from base object to derived object, the base members are not lost. So the following statement will work after conversion:

        dObjPtr->bMthd();

However, be warned: the static_cast operator is not as reliable as the dynamic_cast operator.

reinterpret_cast
The dynamic-cast and static_cast operators convert related objects. However, the reinterpret_cast operator converts any instantiated object to another. It also works using pointers (and references) to the source and target objects. In simple terms its syntax is,

    targetObjPtr = dynamic_cast<targetClassName*>(&sourceObj);

Here, conversion does not really take place; a simple binary copy of the source object is made to the target object. Such a conversion is very unreliable and is not really useful.

const_cast
The aim of the const_cast operator is to remove the constant feature of an object during conversion. It also works using pointers (and references) to the source and target objects. In simple terms its syntax is:

    targetObjPtr = const_cast <targetClassName*>(&sourceObj);

As with the other casting operators, you end up with two objects; the target object is a kind of copy of the source object and the source object is not changed. With all casting operators, the source object is not changed. For simplicity, I illustrate the use of the const_cast operator using fundamental objects. Read and try the following code:

#include <iostream>
using namespace std;

int main()
    {
        int const myInt = 5;
        int *intPtr;

        intPtr = const_cast <int*>(&myInt);

        cout << *intPtr << "\n";
        *intPtr = 6;
        cout << *intPtr;

        return 0;
    }

The main function starts by having an integer that holds a constant value. Then we have a pointer without any assignment. Next we have the casting to assign the value for the constant int to the pointer. The pointer is now pointing to an object different from the source int object. It has the same value as the constant source value, but it is not constant; it has changed. The rest of the statements illustrate the changing capacity of the pointed object.

This kind of casting can be useful when you have to pass a constant value to a function whose parameter is not constant. The following code illustrates this:

#include <iostream>
using namespace std;

void display(int *intPar)
    {
        cout << *intPar;
    }

int main()
    {
        int const myInt = 5;
        int *intPtr;

        display(const_cast<int*>(&myInt));

        return 0;
    }

Note that the argument in the function call is the const_cast operator with its operands. The return pointer of the operator is the effective argument.

typeid
This operator does not really do conversion. It is used to determine the type of object of an operand. It returns a pointer for the type of object. The syntax to use the typeid operator is

        typeid(operand)

You use it with the equal (==) and not-equal (!=) operators. The following code illustrates its use with fundamental objects:

#include <iostream>
#include <typeinfo>
using namespace std;

int main()
    {
        int a = 7;
        int b = 8;

        if (typeid(a) == typeid(b))
            {
                cout << "The types are the same";
            }        

        return 0;
    }

Note that this typeid operator is found in the typeinfo header file. Note the include preprocessing directive for this header file.

In a similar way the typeid operator can be used with instantiated objects. The following code illustrates this:

#include <iostream>
#include <typeinfo>
using namespace std;

class CA
    {
    };
class CB
    {
    };

CA caObj1;
CA caObj2;

CB cbObj;

int main()
    {
        if (typeid(caObj1) == typeid(caObj2))
            {
                cout << "The types are the same" <<"\n";
            }    
        if (typeid(caObj1) != typeid(cbObj))
            {
                cout << "The types are not the same";
            }    

        return 0;
    }

The direction of operation of the casting operators are left-to-right. Casting operators are implicit, explicit, dynamic_cast, static_cast, reinterpret_cast, const_cast and typeid. The calling of the operators look like function calls, but they are operators.

That is it for casting operators. Let us take a break here and continue in the next part of the series.

Chrys

Related Courses

C++ Course
Relational Database and Sybase
Windows User Interface
Computer Programmer – A Jack of all Trade – Poem
NEXT

Comments

Become the Writer's Fan
Send the Writer a Message