The reason for this operator is that occasionally you may have a need for a value that is constant most of the time but that can be changed occasionally. In such a case, you can declare the value as const and use const_cast when you need to alter the value. This could be done using the general type cast, but the general type cast can also simultaneously change the type:

High bar;

const High * pbar = &bar

...

High * pb = (High *) (pbar);       // valid

Low * pl = (Low *) (pbar);         // also valid

Because the simultaneous change of type and constantness may be an unintentional programming slip, using the const_cast operator is safer.

The const_cast is not all powerful. It can change the pointer access to a quantity, but the effect of attempting to change a quantity that is declared const is undefined. Let’s clarify this statement with the short example shown in Listing 15.19.

Listing 15.19. constcast.cpp

// constcast.cpp -- using const_cast<>

#include

using std::cout;

using std::endl;

void change(const int * pt, int n);

int main()

{

    int pop1 = 38383;

    const int pop2 = 2000;

    cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;

    change(&pop1, -103);

    change(&pop2, -103);

    cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;

    return 0;

}

void change(const int * pt, int n)

{

    int * pc;

    pc = const_cast(pt);

    *pc += n;

}

The const_cast operator can remove the const from const int * pt, thus allowing the compiler to accept the following statement in change():

*pc += n;

However, because pop2 is declared as const, the compiler may protect it from any change, as is shown by the following sample output:

pop1, pop2: 38383, 2000

pop1, pop2: 38280, 2000

As you can see, the calls to change() alter pop1 but not pop2. The pointer in change() is declared as const int *, so it can’t be used to change the value of the pointed-to int. The pointer pc has the const cast away, so it can be used to change the pointed-to value, but only if that value wasn’t itself const. Therefore, pc can be used to alter pop1 but not pop2.

The static_cast operator has the same syntax as the other operators:

static_cast < type-name > (expression)

It’s valid only if type_name can be converted implicitly to the same type that expression has, or vice versa. Otherwise, the type cast is an error. Suppose that High is a base class to Low and that Pond is an unrelated class. Then conversions from High to Low and Low to High are valid, but a conversion from Low to Pond is disallowed:

High bar;

Low blow;

...

High * pb = static_cast (&blow);     // valid upcast

Low * pl = static_cast (&bar);        // valid downcast

Pond * pmer = static_cast (&blow);   // invalid, Pond unrelated

The first conversion here is valid because an upcast can be done explicitly. The second conversion, from a base-class pointer to a derived-class pointer, can’t be done without an explicit type conversion. But because the type cast in the other direction can be made without a type cast, it’s valid to use static_cast for a downcast.

Similarly, because an enumeration value can be converted to an integral type without a type cast, an integral type can be converted to an enumeration value with static_cast. Also you can use static_cast to convert double to int, to convert float to long, and to perform the various other numeric conversions.

The reinterpret_cast operator is for inherently risky type casts. It doesn’t let you cast away const, but it does allow other unsavory things. Sometimes a programmer has to do implementation-dependent, unsavory things, and using the reinterpret_cast operator makes it simpler to keep track of such acts. It has the same syntax as the other three operators:

reinterpret_cast < type-name > (expression)

Here is a sample use:

struct dat {short a; short b;};

long value = 0xA224B118;

dat * pd = reinterpret_cast< dat *> (&value);

cout << hex << pd->a;   // display first 2 bytes of value

Typically, such type casts would be used for low-level, implementation-dependent programming and would not be portable. For example, one system may store the bytes in a multibyte value in a different order than does a second system.

The reinterpret_cast operator doesn’t allow just anything, however. For example, you can cast a pointer type to an integer type that’s large enough to hold the pointer representation, but you can’t cast a pointer to a smaller integer type or to a floating-point type. Another restriction is that you can’t cast a function pointer to a data pointer or vice versa.

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги