|
My Training Period: xx hours
The C and C++ programming skills that supposed to be acquired:
22.1 C Typecasting
(type) expression
or
// look like a function :o) isn’t it? type (expression)
int p; double dou;
// same as p = int (dou); p = (int) dou;
|
There are four casting operators in C++ with their main usage is listed in the following table:
Type caster keyword | Description |
static_cast | To convert non polymorphic types. |
const_cast | To add or remove the const-ness or volatile-ness type. |
dynamic_cast | To convert polymorphic types. |
reinterpret_cast | For type conversion of unrelated types. |
Table 22.1: Type caster |
The syntax is same for the four type cast except the cast name:
name_cast<new_type> (expression)
Where:
name_cast | either one of the static,const,dynamic or reinterpret |
new_type | The result type of the cast. |
expression | Expression to be 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)
Ifnew_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);
A program example:
#include <iostream>
using namespace std;
int main()
{
int sum = 1000;
int count = 21;
double average1 = sum/count;
cout<<"Before conversion = "<<average1<<endl;
double average2 = static_cast<double>(sum)/count;
cout<<"After conversion = "<<average2<<endl;
return 0;
}
Other usage of the static_cast includes the conversion of int to enum, reference of type P& toQ&, 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 theconst-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 asdynamic_cast, because it does not have the run time check, for example, for ambiguous pointer,static_cast may return successful but adynamic_cast pointer will fail.
A program example:
#include <iostream>
using namespace std;
// enum data type
enum color {blue, yellow, red, green, magenta};
int main()
{
int p1 = 3;
cout<<"integer type, p1 = "<<p1<<endl;
cout<<"color c1 = static_cast<color> (p1)"<<endl;
color c1 = static_cast<color> (p1);
cout<<"enum type, c1 = "<<c1<<endl;
return 0;
}
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.
Theconst_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. Avolatile-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 the const_cast
#include <iostream>
using namespace std;
int main()
{
// p = 10 is a constant value, cannot be modified
const int p = 20;
cout<<"const p = "<<p<<"\nq = p + 20 = "<<(p + 20)<<endl;
// the following code should generate error, because
// we try to modify the constant value...
// uncomment, recompile and re run, notice the error...
//p = 15;
//p++;
// remove the const...
int r = const_cast<int&> (p);
// the value of 10 should be modified now...
--r;
cout<<"Removing the const, decrement by 1,\nNew value = "<<r<<endl;
return 0;
}
Another simple program example:
// demonstrates const_cast
#include <iostream>
using namespace std;
struct One
{
// test function...
void funct1()
{ cout<<"Testing..."<<endl;}
};
// const argument, cannot be modified...
void funct2(const One& c)
{
// will generate warning/error...
c.funct1();
}
int main()
{
One b;
funct2(b);
return 0;
}
We have to remove the const of the argument. Change c.funct1(); to the following statements re-compile and re-run the program.
// remove the const...
One &noconst = const_cast<One&> (c);
cout<<"The reference = "<<&noconst<<endl;
noconst.funct1():
Another program example.
// demonstrates the type casting
#include <iostream>
using namespace std;
double funct1(double& f)
{
// do some work here...
f++;
cout<<"f = "<<f<<endl;
// return the incremented value...
return f;
}
// const argument, can't be modified...
void funct2(const double& d)
{
cout<<"d = "<<d<<endl;
// remove the 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;
// first function call...
funct2(c);
return 0;
}
volatile and const removal program example:
// demonstrates the type casting
#include <iostream>
using namespace std;
class One
{
public:
void funct()
{cout<<"Testing..."<<endl;};
};
// const and volatile...
const volatile int* Test1;
// const...
const int* Test2;
void TestConstVol()
{
One Test3;
// remove the const...
const_cast<One&>(Test3).funct();
// remove const and volatile...
const_cast<int*> (Test1);
}
int main()
{
TestConstVol();
return 0;
}
Removing the const this pointer program example
// removing the const-ness of the 'this' pointer
#include <iostream>
using namespace std;
class Test
{
public:
void GetNumber(int);
// read only function...
void DisplayNumber() const;
private:
int Number;
};
void Test::GetNumber(int Num)
{Number = Num;}
void Test::DisplayNumber() const
{
cout<<"\nBefore removing const-ness: "<<Number;
const_cast<Test*>(this)->Number+=2;
cout<<"\nAfter removing const-ness: "<<Number<<endl;
}
int main()
{
Test p;
p.GetNumber(20);
p.DisplayNumber();
return 0;
}
-----------------------------------------------------------------------
This function const-ness removal also can be achieved by using the mutable specifier.
Program example using mutable keyword to modify theconst function member variable.
// using mutable to remove the const-ness of the function...
#include <iostream>
using namespace std;
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 a valid expression...
count = num+=3;
ptr = #
cout<<"After some operation, the new value: "<<*ptr<<endl;
return count;
}
};
int main(void)
{
Test var;
cout<<"Initial value of the argument is: 10"<<endl;
var.funct(10);
return 0;
}
For this part, you must enable the Run-Time Type Information (RTTI) setting of your compiler. 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.
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 usingpublic, private or protected keywords.
Figure 22.1: Simple class hierarchy
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 theA and B sub-objects, is the complete object.
Figure 22.2: Class C with sub-objects B and A
Type conversion from base class pointer to a derived class pointer is calleddowncast.
Type conversion from derived class pointer to a base class pointer, is calledupcast.
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.
Figure 22.3: Upcasting, from Derived2 to Derived1/Base1
// upcast conversion using dynamic_cast
#include <iostream>
using namespace std;
// a base class
class Base1 { };
// a derived class...
class Derived1:public Base1 { };
// another derived class
class Derived2:public Derived1{ };
// dynamic_cast test function...
void funct1()
{
// instantiate an object…
Derived2* Test1 = new Derived2;
// upcasting, from derived class to base class,
// Derived1 is a direct from Base1
// making Test2 pointing to Derived1 sub-object of Test1
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;
// upcasting, from derived class to base class
// Derived2 is an indirect from Base1
Base1* Test3 = dynamic_cast<Derived1*>(Test1);
cout<<"\nBase1* Test3 = dynamic_cast<Derived1*>(Test1);"<<endl;
if(!Test3)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
}
int main()
{
funct1();
return 0;
}
Figure 22.4: void* type, from base to base class
void* type conversion program example.
// if new_name is void*, the result of conversion is
// a pointer to the complete object pointed
// to by the expression void* and dynamic_cast
#include <iostream>
using namespace std;
// a base class
class Base1
{
public:
virtual void funct1(){};
};
// another base class...
class Base2
{
public:
virtual void funct2(){};
};
// dynamic_cast test function...
void funct3()
{
// instantiate objects…
Base1 * Test1 = new Base1;
Base2 * Test2 = new Base2;
// making Test3 pointing to an object of type Base1
void* Test3 = dynamic_cast<void*>(Test1);
cout<<"void* Test3 = dynamic_cast<void*>(Test1);"<<endl;
if(!Test3)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
// making Test3 pointing to an object of type Base2
Test3 = dynamic_cast<void*>(Test2);
cout<<"\nTest3 = dynamic_cast<void*>(Test2);"<<endl;
if(!Test3)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
}
int main()
{
funct3();
return 0;
}
Figure 22.5: Downcast, from Base1 to Derived1 class
// downcast conversion using dynamic_cast
#include <iostream>
using namespace std;
// a base class
class Base1 {
public:
virtual void funct1(){ };
};
// a derived class...
class Derived1:public Base1 {
public:
virtual void funct2(){ };
};
// a dynamic_cast test function...
void funct3()
{
// instantiate objects…
Base1* Test1 = new Derived1;
Base1* Test2 = new Base1;
// making Test1 pointing to Derived1
Derived1* Test3 = dynamic_cast<Derived1*>(Test1);
cout<<"Derived1* Test3 = dynamic_cast<Derived1*>(Test1);"<<endl;
if(!Test3)
cout<<"The conversion is fail..."<<endl;
else
cout<<"The conversion is successful..."<<endl;
// 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();
return 0;
}
Figure 22.6: Multiple conversion, fromDerived3 to Base1
tenouk fundamental of C++ object oriented tutorial
Source code for the program examples are available inC/C++ Typecasting source codes.
Check the best selling C / C++, Object Oriented and pattern analysis books at Amazon.com.