C++ Programming Module22 Tenouk
C++ Programming Module22 Tenouk
TYPECASTING
Abilities
22.1 C Typecasting
- Typecasting is used to convert the type of a variable, function, object, expression or return value to
another type.
- Throughout this tutorial you have encountered many codes that use simple C-style type cast.
- One of the said advantageous of C++ is the type safe feature. During the compile or run time there are
type checking process that not available in C. This can avoid a lot of program bugs and unexpected
logical errors.
- In C an expression, expression, of type type, can be cast to another type by using the following
syntax:
(type) expression or
//look like a function :o) isn’t it?
type (expression)
- For example:
int p;
double dou;
- The previous example used the explicit type conversion that is done by programmers. Integral type
promotion and demotion (automatic type casting, as explained in Module 2); is the implicit type
conversion.
- What ever it is, explicit type conversion should be adopted for good programming habits such as for
troubleshooting and readability.
- The weaknesses in C type cast are listed below:
▪ The syntax is same for every casting operation from simple variables to objects and classes.
For complex type casting, we as well as compiler don’t know the intended purpose of the
casting and this will create ambiguity.
▪ When we do the debugging, it is very difficult to locate the related cast problems, although by
using the tools provided by the compiler, because there are many codes that use parentheses.
▪ It allows us to cast practically any type to any other type. This can create many program
bugs. If the program compiled and run successfully, the result still can contain logical errors.
- The four type casting operators in C++ with their main usage is listed in the following table:
- The syntax is same for the four type cast except the cast name:
name_cast<new_type> (expression)
- Where:
www.tenouk.com
name_cast either one of the static, const, dynamic or reinterpret
new_type The result type of the cast.
expression Expression to be cast
22.2 static_cast
- It allows casting a pointer of a derived class to its base class and vice versa. This cast type uses
information available at compile time to perform the required type conversion.
- The syntax is:
name_cast<new_type> (expression)
- If new_type is a reference type, the result is an lvalue; otherwise, the result is an rvalue
- Explicitly can be used to perform conversion defined in classes as well as performing standard
conversion between basic data types, for example:
int p;
double dou;
p = static_cast<int> (dou);
- Program example:
#include <iostream.h>
#include <stdlib.h>
int main()
{
Output:
- Other usage of the static_cast includes the conversion of int to enum, reference of type P& to
Q&, an object of type P to an object of type Q and a pointer to member to another pointer to member
within the same class hierarchy.
- You also can convert any expression to void using static_cast, which the value of the expression
is discarded.
- static_cast cannot be used to convert the const-ness and volatile-ness (cv qualification),
use const_cast instead and polymorphic types.
- An integral type to enumeration conversion can be done using static_cast. The conversion results
in an enumeration with the same value as the integral type provided the integral type value is within the
range of the enumeration. The value that is not within the range should be undefined.
- Keep in mind that, static_cast is not as safe as dynamic_cast, because it does not have the run
time check, for example, for ambiguous pointer, static_cast may return successful but a
dynamic_cast pointer will fail.
- Program example:
www.tenouk.com
#include <iostream.h>
#include <stdlib.h>
int main()
{
int p1 = 3;
system("pause");
return 0;
}
Output:
22.3 const_cast
- This cast type is used to add to or remove the const-ness or volatile-ness of the expression.
- The syntax is:
const_cast<new_type> (expression)
- new_type and expression must be of the same type except for const and volatile
modifiers. Casting is resolved at compile time and the result is of type new_type.
- A pointer to const can be converted to a pointer to non-const that is in all other respects an identical
type. If successful, the resulting pointer refers to the original object.
- A const object or a reference to const cast results in a non-const object or reference that is
otherwise an identical type.
- The const_cast operator performs similar typecasts on the volatile modifier. A pointer to
volatile object can be cast to a pointer to non-volatile object without otherwise changing the
type of the object. The result is a pointer to the original object. A volatile-type object or a reference
to volatile-type can be converted into an identical non-volatile type.
- Simple integral program example of removing the const-ness:
//demonstrates const_cast
#include <iostream.h>
#include <stdlib.h>
int main()
{
//p = 10 is a constant value, cannot be modified
const int p = 20;
www.tenouk.com
Output:
//Demonstrate const_cast
#include <iostream.h>
#include <stdlib.h>
struct One
{
//test function...
void funct1()
{ cout<<"Testing..."<<endl;}
};
int main()
{
One b;
funct2(b);
system("pause");
return 0;
}
- We have to remove the const of the argument. Change c.funct1(); to the following statements
recompile and rerun the program.
Output:
double funct1(double& f)
{
//do some work here...
f++;
cout<<"f = "<<f<<endl;
//return the incremented value...
return f;
}
www.tenouk.com
//const argument, can't be modified...
void funct2(const double& d)
{
cout<<"d = "<<d<<endl;
//remove const...
//use the non-const argument, making function call...
double value = funct1(const_cast<double&> (d));
//display the returned value...
cout<<"value = "<<value<<endl;
}
int main()
{
double c = 4.324;
Output:
class One
{
public:
void funct()
{cout<<"Testing..."<<endl;};
};
void TestConstVol()
{
One Test3;
//remove const...
const_cast<One&>(Test3).funct();
//remove const and volatile...
const_cast<int*> (Test1);
}
int main()
{
TestConstVol();
system("pause");
return 0;
}
Output:
www.tenouk.com
- Removing the const this pointer program example
class Test
{
public:
void GetNumber(int);
//Read only function...
void DisplayNumber() const;
private:
int Number;
};
int main()
{
Test p;
p.GetNumber(20);
p.DisplayNumber();
system("pause");
return 0;
}
Output:
- This function const-ness removal also can be achieved by using the mutable specifier.
- Program example using mutable keyword to modify the const function member variable.
class Test
{
//using mutable
mutable int count;
mutable const int* ptr;
public:
//Read only function can't
//change const arguments.
int funct(int num = 10) const
{
//should be valid expression...
count = num+=3;
ptr = #
cout<<"After some operation, the new value: "<<*ptr<<endl;
return count;
}
};
www.tenouk.com
int main(void)
{
Test var;
cout<<"Initial value of the argument is: 10"<<endl;
var.funct(10);
system("pause");
return 0;
}
Output:
22.4 dynamic_cast
Note:
For this part, you must enable the Run-Time Type Information (RTTI) setting of your compiler
:o). For Visual C++ .Net: Project menu → your_project_name Properties… → C / C++
folder → Language setting.
- This cast is exclusively used with pointers and references to objects for class hierarchy navigation.
- The syntax:
dynamic_cast<new_type> (expression)
- That means converts the operand expression to an object of type, new_type. The new_type
must be a pointer or a reference to previously defined class type or a pointer to void. The type of
expression must be a pointer if new_type is a pointer or lvalue if new_type is a reference.
- It can be used to cast from a derived class pointer to a base class pointer (upcasting), cast a derived
class pointer to another derived (sibling) class pointer (crosscast) or cast a base class pointer to a
derived class pointer (downcast).
- Differing from other cast, dynamic_cast operator is part of the C++ run time type information
(rtti) tally to the term dynamic instead of static, hence it usage closely related to the polymorphic
classes, classes which have at least one virtual function.
- As you have learned, for non-polymorphic class, use the static_cast.
- The validity or safety of the type casting is checked during the run time, if the pointer being cast is not a
pointer to a valid complete object of the requested type, the value returned is a NULL pointer.
www.tenouk.com
- It is safe if the object being pointed to is of type derived class. The actual object is said to be the
complete object. The pointer to the base class is said to point to a sub-object of the complete object.
- The following diagram is the simple class hierarchy. There are base and derived classes. Derived class
is the class that inherits the base class(s) member variable(s) and function(s) with restrictions
implemented using public, private or protected keywords.
- An object of class C could be depicted as the following diagram. For class C instance, there is a B and
A sub-objects. The instance of class C, including the A and B sub-objects, is the complete object.
- Type conversion from base class pointer to a derived class pointer is called downcast.
- Type conversion from derived class pointer to a base class pointer, is called upcast.
- Another one is crosscast, a cast from a class to a sibling class in class hierarchy or sibling class. Two
classes are siblings if a class is directly or indirectly derived from both of their base classes and one is
not derived from the other. It is a multi inheritance class hierarchy.
- Let do some experiment through program examples starting from the upcasting.
//base class
class Base1 {};
//derived class...
class Derived1:public Base1 {};
www.tenouk.com
Derived1* Test2 = dynamic_cast<Derived1*>(Test1);
cout<<"Derived1* Test2 = dynamic_cast<Derived1*>(Test1);"<<endl;
if(!Test2)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
int main()
{
funct1();
system("pause");
return 0;
}
Output:
//base class
class Base1
{
public:
virtual void funct1(){};
};
www.tenouk.com
cout<<"void* Test3 = dynamic_cast<void*>(Test1);"<<endl;
if(!Test3)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
int main()
{
funct3();
system("pause");
return 0;
}
Output:
//base class
class Base1 {
public:
virtual void funct1(){};
};
//derived class...
class Derived1:public Base1 {
public:
virtual void funct2(){};
};
www.tenouk.com
//should fails coz Test2 pointing
//to Base1 not Derived1, Test4 == NULL
Derived1* Test4 = dynamic_cast<Derived1*>(Test2);
cout<<"\nDerived1* Test4 = dynamic_cast<Derived1*>(Test2);"<<endl;
if(!Test4)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
//reconfirm, should be NULL pointer…
cout<<"Should be NULL pointer = "<<Test4<<endl;
}
int main()
{
funct3();
system("pause");
return 0;
}
Output:
//multiple inheritance
//conversion using dynamic_cast
#include <iostream.h>
#include <stdlib.h>
//base class
class Base1 {};
//derived class...
class Derived3:public Derived1, public Derived2
{
public:
virtual void funct1(){}
};
www.tenouk.com
//part, there should be run time error:-)
Base1* Test2 = dynamic_cast<Base1*>(Test1);
cout<<"Base1* Test2 = dynamic_cast<Base1*>(Test1);"<<endl;
if(!Test2)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
//reconfirm the pointer
cout<<"The pointer should be NULL ==> "<<Test2<<endl;
//---------end comment out----------
int main()
{
funct2();
system("pause");
return 0;
}
Output:
Note:
The next two program examples will generate warning and runtime error if you use a very ‘good’ compiler
:o). The unreliable type conversions have been protected by the compiler during runtime.
www.tenouk.com
#include <stdlib.h>
//base class
class Base1
{
public:
virtual void funct1(){};
};
//derived class...
class Base2
{
public:
virtual void funct4(){};
};
else
cout<<"The conversion is successful..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
delete Test1;
}
int main()
{
www.tenouk.com
funct5();
system("pause");
return 0;
}
Output:
www.tenouk.com
Derived1& Derived1Obj = dynamic_cast<Derived1&>(*Base2Obj);
if(!&Derived1Obj)
cout<<"Conversion is failed!...."<<endl;
else
cout<<"Conversion is OK...."<<endl;
cout<<"The address.."<<&Derived1Obj<<endl;
int main()
{
int *ptr = NULL;
int var;
cout<<"Benchmarking..."<<endl;
cout<<"Address of var = "<<&var<<endl;
//NULL pointer
cout<<"NULL *ptr = "<<ptr<<endl;
cout<<endl;
Output:
www.tenouk.com
- Well, tired playing with type casting huh?
22.5 rtti
- Run time type information/identification (RTTI) is a mechanism which the type of an object can be
determined during the program execution where the type of the object cannot be determined by the
static information.
- It can be applied on the pointers and references. RTTI elements consists of:
typeid( expression )
typeid( type_name )
- You can use typeid to get run-time identification of type_name and expressions. A call to
typeid returns a reference to an object of type const type_info&. The returned object
represents the type of the typeid operand.
- If the typeid operand is a dereferenced pointer or a reference to a polymorphic type (class with
virtual functions), typeid returns the dynamic type of the actual object pointed or referred to in the
expression. If the operand is non-polymorphic, typeid returns an object that represents the static
type. typeid operator can be used with fundamental data types as well as user-defined types.
- If the typeid operand is a dereferenced NULL pointer, the bad_typeid exception handler is
thrown.
- Program example, don’t forget to include the typeinfo.h header file.
int main()
{
char c;
www.tenouk.com
float f;
cout<<typeid(double).name();
cout<<" before "<<typeid(int).name()<<": "<<
(typeid(double).before(typeid(int)) ? T:F)<<endl;
cout<<typeid(A).name();
cout<<" before "<<typeid(B).name()<<": "<<
(typeid(A).before(typeid(B)) ? T:F)<<endl;
system("pause");
return 0;
}
Output:
//derived class...
class Derived : public Test {};
int main(void)
{
//Instantiate Derived type object...
Derived DerivedObj;
//Declare a Derived type pointer
Derived *DerivedPtr;
//Initialize the pointer
DerivedPtr = &DerivedObj;
Output:
www.tenouk.com
- If the expression is dereferencing a NULL pointer, typeid() will throw a bad_typeid exception
handler. If the expression is neither a pointer nor a reference to a base class of the object, the result is a
type_info reference representing the static type of the expression.
- Another program example.
class Base
{
public:
virtual void funct(){}
};
int main()
{
Derived* Test1 = new Derived;
Base* Test2 = Test1;
delete Test1;
system("pause");
return 0;
}
Output:
22.6 reinterpret_cast
- This operator is used to convert any pointer to any other pointer type. It also can be used to convert any
integral type to any pointer type and vice versa.
- Because of the unrelated or ‘random’ type conversion can be done using reinterpret_cast, it can
be easily unsafe if used improperly and it is non portable. It should only be used when absolutely
necessary.
- It cannot be used for const-ness and volatile-ness conversion.
- Can be used to convert for example, int* to char*, or classA to classB, which both class are
unrelated classes, between two unrelated pointers, pointers to members or pointers to functions.
- For null pointer, it converts a null pointer value to the null pointer value of the destination type.
- Program example. If you change the for loop from -10 to 0, the conversion values still same, may
need to use 2's complement.
www.tenouk.com
//unsigned int pointers conversion
#include <iostream.h>
#include <stdlib.h>
int main(void)
{
//array name is a pointer...
int a[10];
Output:
- Keyword explicit used to avoid a single argument constructor from defining an automatic type
conversion.
- A typical explicit usage example is in a collection class in which you can pass the initial size as
constructor argument. For example, you could declare a constructor that has an argument for the initial
size of a stack as shown below:
//simple class
//compiled using visual C++ .Net
#include <iostream>
using namespace std;
class MyStack
{
public:
//create a stack with initial size
MyStack(int initsize);
~MyStack(void);
};
MyStack::MyStack(int initsize)
{
static x;
cout<<"Constructor: Pass #"<<x<<endl;
x++;
}
MyStack::~MyStack(void)
{
static y;
cout<<"Destructor: Pass #"<<y<<endl;
www.tenouk.com
y++;
}
//----main program----
int main()
{
//The initial stack size is 10
MyStack p(20);
Output:
- Here, without explicit keyword the constructor would define an automatic type conversion from
int type to MyStack object type.
- From the program output also, it is clear that the constructor was invoked two times, once for
Mystack with size of 20 and another one with size 30. This is not our intention.
- Then we could assign an integer, 30 to MyStack wrongfully, as shown below:
p = 30;
- The automatic type conversion would convert the integer 30 to Mystack, with 30 elements (size) and
then assign it to p.
- By declaring the int constructor as an explicit, the assignment p = 30; will result an error at compile
time. The following is the program example using explicit keyword.
//simple class
//compiled using visual C++ .Net
#include <iostream>
using namespace std;
class MyStack
{
public:
//create a stack with initial size
explicit MyStack(int initsize);
~MyStack(void);
};
MyStack::MyStack(int initsize)
{
static x;
cout<<"Constructor: Pass #"<<x<<endl;
x++;
}
MyStack::~MyStack(void)
{
static y;
cout<<"Destructor: Pass #"<<y<<endl;
y++;
}
//----main program----
int main()
{
//The initial stack size is 10
MyStack p(20);
//but, there will be new stack objects
www.tenouk.com
//with size of 30!
//p = 30;
cout<<"With the explicit keyword!\n";
return 0;
}
Output:
- You can try un-commenting the p = 30 code, then recompile and re run the program. It should
generate an error.
- Note that explicit also rules out the initialization with type conversion by using the assignment syntax
as shown below:
- The previous program example based on the template of the STL. More information is in Module 24
above.
- Program example compiled using VC++ / VC++ .Net.
class Base
{
public:
virtual void funct(){}
};
int main()
{
Derived* Test1 = new Derived;
Base* Test2 = Test1;
Output:
www.tenouk.com
//**********-typecast.cpp-**********
//upcast conversion using dynamic_cast
#include <iostream>
using namespace std;
//base class
class Base1 {};
//derived class...
class Derived1:public Base1 {};
int main()
{
funct1();
return 0;
}
-------------------------------------------------0o0--------------------------------------------------
1. Check the best selling C/C++, Object Oriented and pattern analysis books at Amazon.com.
www.tenouk.com