ANSI C++ adds four cast operators, with template-style syntax, that more clearly express the intentions of the programmer and make casts easier to spot in the code. These ANSI typecasts are:
19.7.1. static_cast and const_cast
static_cast<DestType>(expr) converts the value expr to type DestType, provided that the compiler knows of an implicit conversion from expr to DestType. All type-checking is done at compile time.
static_cast('A' + 1.0); static_cast(static_cast(y) + 1);
The static_cast operator converts between related types such as one pointer type to another, an enumeration type to an integral type, or a floating-point type to an integral type. These conversions are well defined, portable, and invertible. The compiler can apply some minimal type checking for each static_cast.
static_cast cannot cast away constness. For that you must use const_cast<DestType>(expr), which creates a non-const version of expr.
In that case, the DestType can differ from the type of expr only in the presence or absence of const/volatile.
For an int i, static_cast(i) will create a temporary of type double, which has the value of i. The variable i itself is not changed by this cast.
Example 19.3 contains both kinds of casts.
Example 19.3. src/ansicast/m2k.cpp
// Miles are converted to kilometers. #include QTextStream cin(stdin, QIODevice::ReadOnly); QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); const double m2k = 1.609; // conversion constant inline double mi2km(int miles) { return (miles * m2k); } int main() { int miles; double kilometers; cout << "Enter distance in miles: " << flush; cin >> miles ; kilometers = mi2km(miles); cout << "This is approximately " << static_cast(kilometers) << "km."<< endl; cout << "Without the cast, kilometers = " << kilometers << endl; double* dp = const_cast(&m2k); cout << "m2k: " << m2k << endl; cout << "&m2k: " << &m2k << " dp: " << dp << endl; cout << "*dp: " << *dp << endl; *dp = 1.892; <-- 1 cout << "Can we reach this statement? " << endl; return 0; } Output: Enter distance in miles: 23 This is approximately 37km. Without the cast, kilometers = 37.007 m2k: 1.609 &m2k: 0x8049048 dp: 0x8049048 *dp: 1.609 Segmentation fault
|
Here are some observations regarding the previous example.
Casting Away const
In general, const_cast is only used for const-references and pointers to non-const objects. Using const_cast to change const objects has undefined behavior because const objects may be stored in read-only memory (which the operating system protects). In the case of const int, trying to change it by casting away const depends on compiler optimization techniques, which frequently optimize them out of existance (by doing pre-compilation value replacement). Consider Example 19.4.
Example 19.4. src/casts/constcast1.cpp
#include using namespace std; int main() { const int N = 22; int * pN = const_cast(&N); *pN = 33; cout << N << ' ' << &N << endl; cout << *pN << ' ' << pN << endl; } Output: 22 0xbf91cfa0 33 0xbf91cfa0 |
The above output, obtained with gcc version 4.0.3, could be different on your system, because the behavior is undefined.
In this example we used const_cast to obtain a regular pointer to a const int. Because the const int is in stack storage class, we don't get a segmentation fault by attempting to change the memory. The compiler is unable to "optimize out" the int, and the const_cast tells it not to even try.
Exercises: static_cast and const_cast
1. |
In Example 19.4, try moving the const int N = 22; above or below int main() { Observe and explain the difference in output. |
2. |
Predict the output of Example 19.5. Remove the const_cast from the call to f2() inside f1(), and predict the output again. |
Example 19.5. src/casts/constcast2.cpp
#include void f2(int& n) { ++n; } void f1(const int& n, int m) { if(n < m) f2(const_cast(n)); } using namespace std; int main() { int num1(10), num2(20); f1(num1, num2); cout << num1 << endl; } |
19.7.2. reinterpret_cast
reinterpret_cast is used for casts that are representation- or system-dependent; examples are conversions between unrelated types such as int to pointer or between unrelated pointer types such as int* to double*. It cannot cast away const. reinterpret_casts are dangerous, generally not portable, and should be avoided.
Consider the following situation.
Spam spam; Egg* eggP; eggP = reinterpret_cast(&spam); eggP->scramble();
reinterpret_cast takes some spam and gives us an Egg-shaped pointer, without any concern for type compatibility.
By using eggP, we are reinterpreting the bits of spam as if they were bits of egg. In some countries, this would be sacrilege!
What Is It Really Used For?
Sometimes, a C function returns a void* pointing to a type that is known to the developer. In such a case, a typecast from void* to the actual type is needed. If you are sure it is pointing to an Egg, reinterpret_cast is the appropriate cast to use. There is no compiler or runtime checking on such a cast.
19.7.3. Why Not Use C-style Casts?
C-style casts are deprecated and should not be used anymore. Consider the following situation, quite similar to the previous example.
Apple apple; Orange* orangeP; // other processing steps ... orangeP = (Orange*) &apple; orangeP->squeeze();
The problem is that we can not tell from looking at this code whether the developer is aware that an Apple is not compatible with an Orange. From looking at it, it is unclear whether this is a proper type conversion or a non-portable pointer conversion.
Errors caused by such a cast can be very difficult to understand and correct. If a system-dependent cast is necessary, it is preferable to use reinterpret_cast over a C-style cast so that, when troubles arise, it will be easier to spot the likely source of those troubles in the code.
Part I: Introduction to C++ and Qt 4
C++ Introduction
Classes
Introduction to Qt
Lists
Functions
Inheritance and Polymorphism
Part II: Higher-Level Programming
Libraries
Introduction to Design Patterns
QObject
Generics and Containers
Qt GUI Widgets
Concurrency
Validation and Regular Expressions
Parsing XML
Meta Objects, Properties, and Reflective Programming
More Design Patterns
Models and Views
Qt SQL Classes
Part III: C++ Language Reference
Types and Expressions
Scope and Storage Class
Statements and Control Structures
Memory Access
Chapter Summary
Inheritance in Detail
Miscellaneous Topics
Part IV: Programming Assignments
MP3 Jukebox Assignments
Part V: Appendices
MP3 Jukebox Assignments
Bibliography
MP3 Jukebox Assignments