Typecasting in C++ allows us to perform conversion between types.
C-Style Cast
This was carried forward from C- language. Compiler does not perform type checking and hence this type of cast can result in run time errors/ undefined behavior. Therefore It is best to avoid them in the program. For example, consider the following.
Example 1
| #include <iostream> | |
| #include <string> | |
| using namespace std; | |
| struct A{ | |
| int first; | |
| int second; | |
| A(int first, int second):first(first), second(second){} | |
| }; | |
| struct B{ | |
| string third; | |
| string fourth; | |
| }; | |
| int main() { | |
| A* a = new A(1,2); | |
| B* b = (B*)a; | |
| cout<<b->third; //Run-time error | |
| return 0; | |
| } |
Example 2
| #include <iostream> | |
| using namespace std; | |
| int main(){ | |
| char a = 'c'; | |
| int *b = (int*)&a; | |
| cout<<*b; //Undefined behavior because integer pointer tries to access 4 bytes of memory although only 1 byte belongs to it. | |
| return 0; | |
| } |
C++ introduced four new casting operators – static_cast, dynamic_cast, const_cast and reinterpret_cast.
static_cast
static_cast addresses many of the pitfalls associated with the C-style cast. It performs thorough type checking at compile time and throws an error when there is a type mismatch. The earlier examples can therefore be changed to the following. Both the programs will throw compile time errors.
Example 1
| #include <iostream> | |
| #include <string> | |
| using namespace std; | |
| struct A{ | |
| int first; | |
| int second; | |
| A(int first, int second):first(first), second(second){} | |
| }; | |
| struct B{ | |
| string third; | |
| string fourth; | |
| }; | |
| int main() { | |
| A* a = new A(1,2); | |
| B* b = static_cast<B*>(a); //Compiler Error | |
| cout<<b->third; | |
| return 0; | |
| } |
Example 2
| #include <iostream> | |
| using namespace std; | |
| int main(){ | |
| char a = 'c'; | |
| int *b = static_cast<int*>(&a); //Compiler Error | |
| cout<<*b; | |
| return 0; | |
| } |
static_cast does not work well with inheritance. When we have a pointer to a base class that may or may not be pointing to an object of its derived class and we try to explicitly convert this pointer to a pointer of the derived class, static_cast will happily do the conversion for us. This is not desirable as the base class pointer may as well be pointing to an object of the base class.
| #include <iostream> | |
| using namespace std; | |
| class Base_Class { | |
| public: | |
| virtual void func() {} | |
| }; | |
| class Derived_Class :public Base_Class { | |
| public: | |
| void func() {} | |
| }; | |
| int main() { | |
| Base_Class * a = new Base_Class(); | |
| Derived_Class *b = static_cast<Derived_Class*>(a); | |
| if (!b) cout << "b is nullptr"; | |
| else cout << "b is not nullptr"; | |
| cin.get(); | |
| } |
Output:
b is not nullptr
dynamic_cast
dynamic_cast addresses the pitfalls associated with compile time type checking. It performs type checking during run-time and is able to catch the above incorrect type conversion. dynamic_cast returns a null pointer when the cast performed is illegal.
| #include <iostream> | |
| using namespace std; | |
| class Base_Class { | |
| public: | |
| virtual void func() {} | |
| }; | |
| class Derived_Class :public Base_Class { | |
| public: | |
| void func() {} | |
| }; | |
| int main() { | |
| Base_Class * a = new Base_Class(); | |
| Derived_Class *b = dynamic_cast<Derived_Class*>(a); | |
| if (!b) cout << "b is nullptr"; | |
| else cout << "b is not nullptr"; | |
| cin.get(); | |
| } |
Output:
b is nullptr
There are two other casting operators in C++: const_cast and reinterpret_cast. const_cast allows us to remove the ‘const’ and ‘volatile’ properties of types. It works with pointers and references.
Leave a comment