C++ Programming/Type Casting
From Wikibooks, the open-content textbooks collection
Contents |
[edit] Type Checking
Type checking is the process of verifying and enforcing the constraints of types, which can occur at either compile-time or run-time. Compile time checking, also called static type checking, is carried out by the compiler when a program is compiled. Run time checking, also called dynamic type checking, is carried out by the program as it is running. A programming language is said to be strongly typed if the type system ensures that conversions between types must be either valid or result in an error. A weakly typed language on the other hand makes no such guarantees and generally allows automatic conversions between types which may have no useful purpose. C++ falls somewhere in the middle, allowing a mix of automatic type conversion and programmer defined conversions, allowing for almost complete flexibility in interpreting one type as being of another type. Converting variables or expression of one type into another type is called type casting.
[edit] Type Conversion
Type conversion (often a result of type casting) refers to changing an entity of one data type into another. This is done to take advantage of certain features of type hierarchies. For instance, values from a more limited set, such as integers, can be stored in a more compact format and later converted to a different format enabling operations not previously possible, such as division with several decimal places' worth of accuracy. In the object-oriented programming paradigm, type conversion allows programs also to treat objects of one type as one of another. One must do it carefully as type casting can lead to loss of data.
[edit] Automatic Type conversion
Automatic Type conversion happens whenever the compiler expects data of a particular type, but the data is given as a different type, leading to an automatic conversion by the compiler, if the conversion is impossible it will result in an error at compile time or a warning in case of undefined behavior (ie: converting an int to a char). Warnings may vary depending on the compiler used or compiler options.
int a = 5.6; float b = 7;
In the example above, in the first case an expression of type float is given and automatically interpreted as an integer. In the second case (more subtle), an integer is given and automatically interpreted as a float.
There are two types of automatic type conversions between numeric types: promotion and demotion. (Note: the term "demotion" is not normally used in the C++ community.)
[edit] Promotion
Promotion occurs whenever a variable or expression of a smaller type is converted to a larger type.
// promoting float to double..... float a = 4; // 4 is an int constant, gets promoted to float long b = 7; // 7 is an int constant, gets promoted to long double c = a; // a is a float, gets promoted to double
There is generally no problem with automatic promotion. Programmers should just be aware that it happens.
[edit] Demotion
Demotion occurs whenever a variable or expression of a larger type gets converted to a smaller type. By default, a floating point number is considered as a double number in C++.
// Demotion of float to char..... int a = 7.5; // double gets down-converted to int; int b = 7.0f; // float gets down-converted to int; char c = b; // int gets down-converted to char;
Automatic demotion can result in the loss of information. In the first example the variable a will contain the value 7, since int variables cannot handle floating point values.
Most modern compilers will generate a warning if demotion occurs. Should the loss of information be intended, the programmer may do explicit type casting to suppress the warning; bit masking may be a superior alternative.
[edit] Explicit type conversion (casting)
Explicit type conversion (casting) is the use of direct and specific notation in the source code to request a conversion or to specify a member from an overloaded class. There are cases where no automatic type conversion can occur or where the compiler is unsure about what type to convert to, those cases require explicit instructions from the programmer or will result in error.
[edit] The basic form of type cast
The basic explicit form of typecasting is the static cast.
A static cast looks like this:
static_cast<target type>(expression)
The compiler will try its best to interpret the expression as if it would be of type type. This type of cast will not produce a warning, even if the type is demoted.
int a = static_cast<int>(7.5);
The cast can be used to suppress the warning as shown above. static_cast cannot do all conversions; for example, it cannot remove const qualifiers, and it cannot perform "cross-casts" within a class hierarchy. It can be used to perform most numeric conversions, including conversion from a integral value to an enumerated type.
[edit] Advanced type casts
[edit] const_cast
const_cast<T>(expression)
The const_cast<>() is used to add/remove const(ness) (or volatile-ness) of a variable.
[edit] static_cast
static_cast<T>(expression)
The static_cast<>() is used to cast between numeric types.
'e.g.' char->long, int->short, double->int, etc.
Static cast is also used to cast pointers to related types. For example, it can cast void* to the appropriate pointer type or vice-versa.
[edit] dynamic_cast
Dynamic cast is used to convert pointers and references at run-time, generally for the purpose of casting a pointer or reference up or down an inheritance chain (inheritance hierarchy).
dynamic_cast<target type>(expression)
The target type must be a pointer or reference type, and the expression must evaluate to a pointer or reference. Dynamic cast works only when the type of object to which the expression refers is compatible with the target type and the base class has at least one virtual member function. If not, and the type of expression being cast is a pointer, NULL is returned. If a dynamic cast on a reference fails, a bad_cast exception is thrown. When it doesn't fail, dynamic cast returns a pointer or reference of the target type to the object to which expression referred.
[edit] reinterpret_cast
Reinterpret cast simply casts one type bitwise to another. Any pointer or integral type can be casted to any other with reinterpret cast, easily allowing for misuse. For instance, with reinterpret cast one might, unsafely, cast an integer pointer to a string pointer.
reinterpret_cast<target type>(expression)
The reinterpret_cast<>() is used for all non portable casting operations. This makes it simpler to find these non portable casts when porting an application from one OS to another.
The reinterpret_cast<T>() will change the type of an expression without altering its underlying bit pattern. This is useful to cast pointers of a particular type into a void* and subsequently back to the original type.
[edit] Older forms of type casts
Other common type casts exist. They are of the form type(expression) (a functional, or function-style, cast) or (type)expression (often known simply as a C-style cast). The format of (type)expression is more common in C (where it is the only cast notation). It has the basic form:
int i = 10; long l; l = (long)i; //C style l = long(i); //C++ style //note: initializes a new long to i, this is not an explicit cast as in the example above //however an implicit cast does occur. i = long((long)i);
The more recent keyword casts are more controlled, and should generally be preferred. Some will make the code safer since they will enable to catch more errors at compile-time, and all are easier to search and identify in code. Performance wise they are the same with the exception of dynamic_cast, for which there is no C equivalent.
[edit] Common usage of type casting
Performing arithmetical operations with varying types of data type without an explicit cast means that the compiler has to perform an implicit cast to ensure that the values it uses in the calculation are of the same type. Usually, this means that the compiler will convert all of the values to the type of the value with the highest precision.
The following is an integer division and so a value of 2 is returned.
float a = 5 / 2;
To get the intended behavior, you would either need to cast one or both of the constants to a float.
float a = static_cast<float>(5) / static_cast<float>(2);
Or, you would have to define one or both of the constants as a float.
float a = 5f / 2f;
[edit] Summary of different casts
reinterpret_cast - mostly non-portable way to convert without changing the representation of a value
int a = 0xffe38024; int * b = reinterpret_cast<int*>(a);
static_cast - pointer casts from base to derived class, or void* to target type*
BaseClass* a = new DerivedClass(); static_cast<DerivedClass*>(a)->derivedClassMethod();
const_cast - changes a const qualifier
struct A { void func() {} }; void f(const A& a) { A& b = const_cast<A&>(a); b.func(); }
dynamic_cast - similar to static_cast, but has a runtime check which ensures that the object is really of the derived type you're casting to, and is also capable of navigating multiple inheritance hierarchies, including performing so-called "cross casts":
class A { ... }; class B : public A { ... }; void f(A* a) { B* b = dynamic_cast<B*>(a); // Won't compile B* b = static_cast<B*>(a); // Will compile }
class A { virtual void foo() {} }; class B : public A { ... }; void f(A* a) { B* b = dynamic_cast<B*>(a); // Will compile B* b = static_cast<B*>(a); // Will compile }