Casting pointer to derived class and vice versa

T

Taras_96

Hi everyone,

I was experimenting with static_cast and reinterpret cast

#include <iostream>

struct A1 { int a; };
struct A2 { double d; };

struct B : public A1, A2
{
int v;
};

int main()
{
// one B object
B b;

// two A2 pointers
A2 * p1;
A2 * p2;

// set both pointers to &b

p1 = static_cast<A2*>(&b);
p2 = reinterpret_cast<A2*>(&b);

// same type, same B object...
// but point to different addresses:
std::cout << "p1: " << p1 << "\n";
std::cout << "p2: " << p2 << "\n";

// the pointers are not equal
assert (p1 == p2); // fails
}

Program Output:

p1: 0xbffff97c
p2: 0xbffff978
Assertion failed: (p1 == p2), function main, file test.cc, line
30.
Abort trap

AFAICT, the difference between the two casts is that static_cast knows
that you're casting down, so it adjusts the value of the pointer
accordingly. reinterpret_cast is just a brute cast.

Previous to this, I thought that different pointer types to the same
underlying object would still store the same address, but the
differing pointer types was more of an indication to the compiler what
expressions the pointer could support. It seems from this experiment
that this is incorrect. Is the reason why the derived pointer moves up
in memory is because of the memory layout, something like this:


TOP OF STACK
------------ <- pointer to derived class
derived members can also 'see' base class members,
. if we think of visibility
. as extending downwards
------------ <- pointer to base can not 'see' derived
members
base members
------------
BOTTOM OF STACK

Is this even defined in C++, or is it implementation specific?

Interestingly when you assign a derived pointer to a base pointer, the
value stored doesn't change, even with a static_cast. This is more
inline with how I thought pointers would originally behave.

So casting from a base to a derived will change the value stored by
the pointer, but casting (or assigning) from a derived to a base will
not change the value. Is there a reason for this?

Thanks

Taras
 
N

Noah Roberts

Taras_96 wrote:
Is the reason why the derived pointer moves up
in memory is because of the memory layout, something like this:


TOP OF STACK
------------ <- pointer to derived class
derived members can also 'see' base class members,
. if we think of visibility
. as extending downwards
------------ <- pointer to base can not 'see' derived
members
base members

It's implementation specific. You can pretty much be sure that
something 'like' this is happening though.
 
S

sk_usenet

Taras_96 said:
I was experimenting with static_cast and reinterpret cast

#include <iostream>

struct A1 { int a; };
struct A2 { double d; };

struct B : public A1, A2
{
int v;
};

int main()
{
// one B object
B b;

// two A2 pointers
A2 * p1;
A2 * p2;

// set both pointers to &b

p1 = static_cast<A2*>(&b);
p2 = reinterpret_cast<A2*>(&b);

// same type, same B object...
// but point to different addresses:
std::cout << "p1: " << p1 << "\n";
std::cout << "p2: " << p2 << "\n";

// the pointers are not equal
assert (p1 == p2); // fails
}

Program Output:

p1: 0xbffff97c
p2: 0xbffff978
Assertion failed: (p1 == p2), function main, file test.cc, line
30.
Abort trap

AFAICT, the difference between the two casts is that static_cast knows
that you're casting down, so it adjusts the value of the pointer

Where are you casting down?
accordingly. reinterpret_cast is just a brute cast.

static_cast would make the derived class pointer point to the appropriate
base class (as laid out in memory), hence you see different values.
reinterpret_cast is just a reinterpretation of the bits. Of course this is
implementation defined. You could check "Inside the C++ Object Model" book
by Lippman for more details.

[snip]
Is this even defined in C++, or is it implementation specific?
Implementation defined.
Interestingly when you assign a derived pointer to a base pointer, the
value stored doesn't change, even with a static_cast. This is more
inline with how I thought pointers would originally behave.

What? Is this in line with what you saw in your sample program?
So casting from a base to a derived will change the value stored by
the pointer, but casting (or assigning) from a derived to a base will
not change the value. Is there a reason for this?

Check dynamic_cast for downcasting.
 
S

sk_usenet

Victor Bazarov said:
The meaning of "Down" and "Up" is open for debate. Some consider
the root of the tree to be at the top. I've never seen a real tree
like that. Heard of it, but never seen one.

What is your _interpretation_ of downcasting?
"Hence"? There is no 'static_cast' required to convert from the

Did I imply that a static_cast was essentially required? If so, then thanks
for clarifying it.

[snip]
Again, you seem to use different meaning of "down-" or "up-" AFA
casting is concerned. And 'dynamic_cast' is NOT going to work

True, I overlooked that part. But the intention was to look beyond
static_cast and reinterpret_cast
for so-called "downcasting".
 
T

Taras_96

What? Is this in line with what you saw in your sample program?

That a pointer to an object, regardless of whether the pointer is a
pointer to a base type or a derived type, will always point to the
same place.
 
T

Taras_96

"Hence"? There is no 'static_cast' required to convert from the
derived class to the accessible unambiguous base class. And the
different values are only because the sizes of the base class
subobjects are not 0, so they occupy some space in the derived
class object. Using 'reinterpret_cast' in this situation is
simply not legal.

Could you explain the 'subojbects' bit a bit further? I think that's
the answer to my question. (in the code below, both Derived and Base
have one int member each)

Derived* pDerived = new Derived(); // pDerived points to a Derived
object
Base* pBase = static_cast<Base*>(pDerived); // address hasn't
changed
Derived* pDerived3 = static_cast<Derived*>(pBase); // address hasn't
changed

I found that casting to a base class (even though, AFAIK, the
static_cast isn't required), doesn't change the address, and neither
does casting back up to the Derived class.

Base* pBaseNext = new Derived();
Derived* pDerivedNext = static_cast<Derived*>(pBaseNext);

Both of these values are also the same.

So how come in my first example the values were changing?

Thanks

Taras
 
J

James Kanze

"Hence"? There is no 'static_cast' required to convert from the
derived class to the accessible unambiguous base class. And the
different values are only because the sizes of the base class
subobjects are not 0, so they occupy some space in the derived
class object.

Just a nit but, while your statement is generally true in
practice, the standard doesn't require it. The standard gives
the compiler pretty much unlimited leeway in how it lays things
out when inheritance is involved, and even in the simplest
cases, there's no guarantee that the address of the base class
is the same as the address of the derived class.
Using 'reinterpret_cast' in this situation is simply not
legal.

The reinterpret_cast itself is legal. Doing anything with the
results of it isn't (except for casting it back to the original
type).
 
G

Greg Herlihy

Hi everyone,

I was experimenting with static_cast and reinterpret cast

     #include <iostream>

     struct A1 { int a; };
     struct A2 { double d; };

     struct B : public A1, A2
     {
         int v;
     };

     int main()
     {
         // one B object
         B b;

         // two A2 pointers
         A2 * p1;
         A2 * p2;

         // set both pointers to &b

         p1 = static_cast<A2*>(&b);
         p2 = reinterpret_cast<A2*>(&b);

         // same type, same B object...
         // but point to different addresses:
         std::cout << "p1: " << p1 << "\n";
         std::cout << "p2: " << p2 << "\n";

         // the pointers are not equal
         assert (p1 == p2); // fails
     }

     Program Output:

     p1: 0xbffff97c
     p2: 0xbffff978
     Assertion failed: (p1 == p2), function main, file test.cc, line
30.
     Abort trap

I'm sure that you did not intend to create the impression that you
wrote the above program in the course of your "experimenting" with
various C++ cast operators.

But without properly attributing the origin of the program include in
your post:

http://preview.tinyurl.com/57k6td

readers might well be left with that impression that this (superbly
written - I might add :)) C++ program was yours. I believe that
properly citing one's sources is always the right thing to do - even
in a USENET post.

Greg
 
T

Taras_96

I'm sure that you did not intend to create the impression that you
wrote the above program in the course of your "experimenting" with
various C++ cast operators.

But without properly attributing the origin of the program include in
your post:

http://preview.tinyurl.com/57k6td

readers might well be left with that impression that this (superbly
written - I might add :)) C++ program was yours. I believe that
properly citing one's sources is always the right thing to do - even
in a USENET post.

Greg

Ok, sorry Greg - will cite all source from now :).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top