|
|
My Training Period: xx hours
More Typecasting Program Examples Using C++ Exception Class
// multiple inheritance, conversion using dynamic_cast #include <iostream> using namespace std;
// a base class class Base1 { }; class Derived1:public Base1{ }; class Derived2:public Base1{ }; // a derived class... class Derived3:public Derived1, public Derived2 { public: virtual void funct1(){ } }; // a dynamic_cast test function... void funct2() { // instantiate an object… Derived3 *Test1 = new Derived3;
// -------start comment out--------- // may fail, ambiguous...from Derived3 direct // conversion to Base1... // if you use good compiler, please comment out this // 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; // re-confirm the pointer cout<<"The pointer should be NULL ==> "<<Test2<<endl; // ---------end comment out----------
// solution, traverse, recast, firstly, cast to Derived1 Derived1* Test3 = dynamic_cast<Derived1*>(Test1); cout<<"\nDerived1* Test3 = dynamic_cast<Derived1*>(Test1);"<<endl; if(!Test3) cout<<"The conversion is fail..."<<endl; else cout<<"The conversion is successful..."<<endl;
// then cast to base1.... Base1* Test4 = dynamic_cast<Base1*>(Test3); cout<<"\nBase1* Test4 = dynamic_cast<Base1*>(Test3);"<<endl; if(!Test4) cout<<"The conversion is fail..."<<endl; else cout<<"The conversion is successful..."<<endl; }
int main() { funct2(); return 0; }
Output: |

Let try the crosscast program example.
The next two program examples will generate warning and runtime error if you use a ‘good’ compiler. The unreliable type conversions have been protected by the compiler during runtime.

Figure 22.7: Crosscast, from Base2 to Derived1
// testing the crosscast: downcast, upcast and crosscast
// 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 derived class
class Derived2:public Base1{
public:
virtual void funct3(){ };
};
// a derived class...
class Base2
{
public:
virtual void funct4(){ };
};
// a derived class
class Derived3:public Derived1,public Derived2,public Base2
{ };
// dynamic_cast test function...
void funct5()
{
// instantiate an object,Test1 of type Base2...
// or test1 of type Derived2...you can choose either one
Base2* Test1 = new Base2;
// Derived2* Test1 = new Derived2;
// start with downcast, type Base2/Derived2 to Derived3...
Derived3* Test2 = dynamic_cast<Derived3*>(Test1);
cout<<"Firstly, Derived3* Test2 = dynamic_cast<Derived3*>(Test1);"<<endl;
if(!Test2)
{
cout<<"The conversion is fail lor!"<<endl;
cout<<"Checking the pointer = "<<Test2<<endl;
}
else
cout<<"The conversion is successful..."<<endl;
// upcast, type derived3 to type derived1...
Derived1* Test3 = dynamic_cast<Derived1*>(Test2);
cout<<"\nThen, Derived1* Test3 = dynamic_cast<Derived1*>(Test2);"<<endl;
if(!Test3)
{
cout<<"The conversion is fail lor!"<<endl;
cout<<"Checking the pointer = "<<Test3<<endl;
}
else
cout<<"The conversion is successful..."<<endl;
// crosscast, direct, type Base2/Derived2 to Derived1...
Derived1* Test4 = dynamic_cast<Derived1*>(Test1);
cout<<"\nThen, Derived1* Test4 = dynamic_cast<Derived1*>(Test1);"<<endl;
if(!Test4)
{
cout<<"The conversion is fail lor!"<<endl;
cout<<"Checking the pointer = "<<Test3<<endl;
}
else
cout<<"The conversion is successful..."<<endl;
delete Test1;
}
int main()
{
funct5();
return 0;
}

Another tough program example.

// a dynamic_cast ambiguous conversion experiment :o)
#include <iostream>
using namespace std;
// a class with virtual function, polymorphic…
class Base1
{
public:
virtual void FuncBase1()
{ };
};
// another class with virtual function...
class Base2
{
public:
virtual void FuncBase2()
{ };
};
// derived class from Base1 and Base2 classes
// public virtual and private...
class Derived1:public virtual Base1, private Base2
{ };
// a dynamic_cast test function...
void DynamicCastSample()
{
// instantiate an object of type Derived1 class...
Derived1 DerivedObj;
// simple assignment, derived to base class, upcasting...
// cast needed to break private protection...
Base2* Base2Obj = (Base2*) &DerivedObj;
// another assignment, derived to base class, upcasting...
// public inheritance, no need casting..
Base1* Base1Obj = &DerivedObj;
// base class to derived class, downcast
Derived1& Derived1Obj = dynamic_cast<Derived1&>(*Base2Obj);
if(!&Derived1Obj)
cout<<"Conversion is failed!...."<<endl;
else
cout<<"Conversion is OK...."<<endl;
cout<<"The address.."<<&Derived1Obj<<endl;
// base class to derived class, downcast
Base1Obj = dynamic_cast<Base1*>(Base2Obj);
if(!Base1Obj)
cout<<"Conversion is failed!...."<<endl;
else
cout<<"Conversion is OK...."<<endl;
cout<<"The address.."<<Base1Obj<<endl;
// base class to base class, ????
// no inheritance…
Base2Obj = dynamic_cast<Base2*>(Base1Obj);
if(!Base2Obj)
cout<<"Conversion is failed!...."<<endl;
else
cout<<"Conversion is OK...."<<endl;
cout<<"The address.."<<Base2Obj<<endl;
// derived class to base class, upcast
Base1Obj = dynamic_cast<Base1*>(&Derived1Obj);
if(!Base1Obj)
cout<<"Conversion is failed!...."<<endl;
else
cout<<"Conversion is OK...."<<endl;
cout<<"The address.."<<Base1Obj<<endl;
// derived class to base class...
// Derived1Obj is derived from non-virtual, private Base2…
Base2Obj = dynamic_cast<Base2*>(&Derived1Obj);
if(!Base2Obj)
cout<<"Conversion is failed!...."<<endl;
else
cout<<"Conversion is OK...."<<endl;
cout<<"The address.."<<Base2Obj<<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;
// call the function for dynamic_cast testing...
DynamicCastSample();
return 0;
}
|
Well, tired playing with type casting huh?
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:
RTTI element | Brief description |
dynamic_cast | Polymorphic types conversion. |
typeid() operator | Used to identify the exact type of an object. |
type_info class | Used for holding the type information returned by the typeid operator. |
Table 22.2: RTTI elements | |
Thetypeid operator syntax:
typeid( expression )
typeid( type_name )
You can usetypeid to get run-time identification oftype_name and expressions. A call totypeid returns a reference to an object of type const type_info&. The returned object represents the type of the typeid operand.
If thetypeid 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 thetypeid operand is a dereferencedNULL pointer, the bad_typeid exception handler is thrown.
A program example, don’t forget to include the typeinfo header file.
// using typeid operator, type_info::before()
// and type_info::name() member functions
#include <iostream>
#include <typeinfo>
using namespace std;
// T - True, F - False
#define T 1
#define F 0
// a base class
class A { };
// a derived class
class B : A { };
int main()
{
char c;
float f;
// using typeinfo operator, == for comparison
if (typeid(c) == typeid(f))
cout<<"c and f are the same type."<<endl;
else
cout<<"c and f are different type."<<endl;
// using true and false comparison...
// name() and before() are typeinfo member functions...
cout<<typeid(int).name();
cout<<" before "<<typeid(double).name()<<": "<<
(typeid(int).before(typeid(double)) ? T:F)<<endl;
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;
return 0;
}

Another program example:
// getting the run time type information...
#include <iostream>
#include <typeinfo>
using namespace std;
// polymorphic base class...
class __rtti Test
{
// this makes Test a polymorphic class type.
virtual void func() { };
};
// a 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;
// do the run time checking...
if(typeid(*DerivedPtr) == typeid(Derived))
// check the type of *DerivedPtr
cout<<"Ptr *DerivedPtr type name is "<<typeid(*DerivedPtr).name();
if(typeid(*DerivedPtr) != typeid(Test))
cout<<"\nPointer DerivedPtr is not a Test class type.\n";
return 0;
}

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.
// run time type information...
#include <iostream>
#include <typeinfo>
using namespace std;
class Base
{
public:
virtual void funct(){ }
};
class Derived:public Base{ };
int main()
{
Derived* Test1 = new Derived;
Base* Test2 = Test1;
cout<<"The type name of Test1 is: ";
cout<<typeid(Test1).name()<<endl;
cout<<"The type name of *Test1 is: ";
cout<<typeid(*Test1).name()<<endl;
cout<<"The type name of Test2 is: ";
cout<<typeid(Test2).name()<<endl;
cout<<"The type name of *Test2 is: ";
cout<<typeid(*Test2).name()<<endl;
delete Test1;
return 0;
}

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 usingreinterpret_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* tochar*, 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.
A program example. If you change the for loop from -10 to 0, the conversion values still same, may need to use 2's complement.
// using reinterpret_cast, int to
// unsigned int pointers conversion
#include <iostream>
using namespace std;
unsigned int* Test(int *q)
{
// convert int pointer to unsigned int pointer
unsigned int* code = reinterpret_cast<unsigned int*>(q);
// return the converted type data, a pointer...
return code;
}
int main(void)
{
// array name is a pointer...
int a[10];
cout<<"int pointer unsigned int pointer"<<endl;
for(int i = 0;i<=10;i++)
cout<<(a+i)<<" converted to "<<Test(a+i)<<endl;
return 0;
}

Keywordexplicit 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:
// a 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;
y++;
}
// the main program
int main()
{
// the initial stack size is 10
MyStack p(20);
// but, there will be new stack objects with size of 30!
p = 30;
cout<<"Without the explicit keyword!\n";
return 0;
}

Here, withoutexplicit 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 assignmentp = 30; will result an error at compile time. The following is the program example using explicit keyword.
// a 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);
};
// constructor
MyStack::MyStack(int initsize)
{
static x;
cout<<"Constructor: Pass #"<<x<<endl;
x++;
}
// destructor
MyStack::~MyStack(void)
{
static y;
cout<<"Destructor: Pass #"<<y<<endl;
y++;
}
// the main program
int main()
{
// the initial stack size is 10
MyStack p(20);
// but, there will be new stack objects with size of 30!
// p = 30;
cout<<"With the explicit keyword!\n";
return 0;
}

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:
MyStack p1(30); // OK
MyStack p2 = 30; // error
The previous program example based on the template of the STL. More information is inC++ Templates above.
// run time type information, compiled using VC++/VC++ .Net
#include <iostream>
#include <typeinfo>
usingnamespace std;
class Base
{
public:
virtualvoid funct(){}
};
class Derived:public Base{};
int main()
{
Derived* Test1 = new Derived;
Base* Test2 = Test1;
cout<<"The type name of Test1 is: ";
cout<<typeid(Test1).name()<<endl;
cout<<"The type name of *Test1 is: ";
cout<<typeid(*Test1).name()<<endl;
cout<<"The type name of Test2 is: ";
cout<<typeid(Test2).name()<<endl;
cout<<"The type name of *Test2 is: ";
cout<<typeid(*Test2).name()<<endl;
delete Test1;
return 0;
}

Program example compiled usingg++.
// **********-typecast.cpp-**********
// upcast conversion using dynamic_cast
#include <iostream>
using namespace std;
// base class
class Base1 { };
// 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;
}
[bodo@bakawali ~]$ g++ typecast.cpp -o typecast
[bodo@bakawali ~]$ ./typecast
Derived1* Test2 = dynamic_cast<Derived1*>(Test1);
The conversion is successful...
Base1* Test3 = dynamic_cast<Derived1*>(Test1);
The conversion is successful...
tenouk fundamental of the 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.