Eduard Rostomyan
Eduard Rostomyan

Reputation: 6546

Casting to different Base classes gives different result. C++

Maybe my question is not perfectly formed, but my code will make everything clear.

#include <iostream>
using namespace std;

struct A{int n;};
struct B{int n;};
struct C : A, B{};

int main()
{
    C c;
    C* pc = &c;

    std::cout<<"TEST1"<<std::endl;
    cout << static_cast<B*>(pc) << "\n";
    cout << reinterpret_cast<B*>(pc)<<"\n\n";

    std::cout<<"TEST2"<<std::endl;
    cout << static_cast<A*>(pc) << "\n";
    cout << reinterpret_cast<A*>(pc)<<"\n";
}

And the output is:

TEST1
0042F830
0042F82C

TEST2
0042F82C
0042F82C

I know that using reinterpret_cast is ill formed design. I am not thinking about the design but the behavior is what bother me. Can anyone explain why casting different ways gives different results the first time but the same result the second time??

Upvotes: 3

Views: 141

Answers (2)

Olaf Dietsche
Olaf Dietsche

Reputation: 74018

Defining a class also means defining a memory layout. In its simplest form the members are laid out consecutively, e.g.

struct A {
    int n;
};

and in memory

| Address  | Size | Member |
|----------+------+--------+
| 0042F82C | 4    | n      |

The same happens with base classes

struct C : A, B {
};

potential memory layout

| Address  | Size | Member |
|----------+------+--------+
| 0042F82C | 4    | A::n   |
| 0042F830 | 4    | B::n   |

Now you have a pointer pc to an object of type C. Using static_cast takes into account the layout of members and base classes within an object.

Therefore, you get the proper address for the A or B part.

Using reinterpret_cast on the other side, just reuses a pointer and pretends it points to another type.

Explanation

Unlike static_cast, but like const_cast, the reinterpret_cast expression does not compile to any CPU instructions. It is purely a compiler directive which instructs the compiler to treat the sequence of bits (object representation) of expression as if it had the type new_type.

This is the reason, why you get the same address value.

Upvotes: 2

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

Reputation: 29013

Essentially, the A and B portions of C can't occupy the same space. One must come before the other. When you properly cast a C* to a A*, you get a pointer to the A portion of the instance that the original pointer pointed to, and the same is true for casting to B*. Since the A part of C (int A::n;) and the B part of C (int B::n;) are necessarily at different addresses, it's natural that the result of those conversions are also different from each other. This is possible because the compiler can know the layout of the object pointed to by pc, the information is deducible from it's type. This wouldn't work if the information wasn't available, for example if the pointer was cast to void* first.

The reason that reinterpret_cast gives the same address regardless of what you cast to is because that's specifically what reinterpret_cast does. It converts a pointer or reference to another type while disregarding any form of type safety. To reinterpret_cast a pointer is to create a pointer of a new type with the same address as the one provided, regardless of actual types and type safety.

Beware of using reinterprect_cast, as it essentially injects facts into the type safety system. The compiler is bound to assume that what you are telling it is correct. If those "facts" aren't true (like in the case of reinterpret_cast<B*>(pc)) you risk undefined behavior.

Upvotes: 3

Related Questions