Simple class inheritance question

C

crea

I know its simple, but I cannot find the answer from books because its a
special case.
Here:
///////////////////////
class A
{
public:
A* a;
};

class B : public A
{
public:
int var;
};

A a1, a2;

a1.a = &a2;
((B*)a1.a)->var = 77;
////////////////
I compiled and it works.

How/why can a1.a set B classes member variable's value even though there is
not even a one object created from B? How is it possible.

So all objects are from class A and there is no object created from B and we
are still using B's members. I thought that this var-variable is not even
allocated from the memory because there is no B object created??
 
R

Richard

[Please do not mail me a copy of your followup]

Leigh Johnston <[email protected]> spake the secret code
Just because it compiles and seems to work does not mean that it is
correct; it is incorrect (a bug) as it invokes UB.


It is not possible; what you have written is buggy code.


You are illegally overwriting stack space which doesn't seem to manifest
any ill effects at the moment but may in the future.

IIRC, the error would manifest itself as a runtime crash if you had
used dynamic_cast<B *>(a1.a) instead of a C-style cast. The C-style
cast has no safeguards and basically says "just jam it in there, I
know what I'm doing (even when I don't)".
 
C

crea

Leigh Johnston said:
Just because it compiles and seems to work does not mean that it is
correct; it is incorrect (a bug) as it invokes UB.


It is not possible; what you have written is buggy code.

Seriously, I am telling the truth: I have Visual C++ 2008, and it compiles
and lets me to use it as normal parameter after that. If I call
((B*)a1.a)->var later on, it will give 77!
 
C

crea

crea said:
Seriously, I am telling the truth: I have Visual C++ 2008, and it compiles
and lets me to use it as normal parameter after that. If I call
((B*)a1.a)->var later on, it will give 77!

anyway, am happy to know it was not legal coding... because thats what i
though as well. Maybe not a good idea to learn C++ by just compiling code
:).
 
I

Ian Collins

Seriously, I am telling the truth: I have Visual C++ 2008, and it compiles
and lets me to use it as normal parameter after that. If I call
((B*)a1.a)->var later on, it will give 77!

What part of "Just because it compiles and seems to work does not mean
that it is correct; it is incorrect (a bug) as it invokes UB." didn't
you get?
 
P

Paul

crea said:
I know its simple, but I cannot find the answer from books because its a
special case.
Here:
///////////////////////
class A
{
public:
A* a;
};

class B : public A
{
public:
int var;
};

A a1, a2;

a1.a = &a2;
((B*)a1.a)->var = 77;
////////////////
I compiled and it works.

How/why can a1.a set B classes member variable's value even though there
is not even a one object created from B? How is it possible.

So all objects are from class A and there is no object created from B and
we are still using B's members. I thought that this var-variable is not
even allocated from the memory because there is no B object created??

Basically you're dereferencing an uninitialised pointer.

B* b;
b->var=77;

It just happens that the pointer is a member of a class, and you've
type-casted it. The pointer could point to anything. °_°
 
R

Richard

[Please do not mail me a copy of your followup]

Leigh Johnston <[email protected]> spake the secret code
[Please do not mail me a copy of your followup]

Leigh Johnston<[email protected]> spake the secret code
On 04/04/2011 23:11, crea wrote:
I know its simple, but I cannot find the answer from books because its a
special case.
Here:
///////////////////////
class A
{
public:
A* a;
};

class B : public A
{
public:
int var;
};

A a1, a2;

a1.a =&a2;
((B*)a1.a)->var = 77;
////////////////
I compiled and it works.me

Just because it compiles and seems to work does not mean that it is
correct; it is incorrect (a bug) as it invokes UB.


How/why can a1.a set B classes member variable's value even though there is
not even a one object created from B? How is it possible.

It is not possible; what you have written is buggy code.


So all objects are from class A and there is no object created from B and we
are still using B's members. I thought that this var-variable is not even
allocated from the memory because there is no B object created??

You are illegally overwriting stack space which doesn't seem to manifest
any ill effects at the moment but may in the future.

IIRC, the error would manifest itself as a runtime crash if you had
used dynamic_cast<B *>(a1.a) instead of a C-style cast. The C-style
cast has no safeguards and basically says "just jam it in there, I
know what I'm doing (even when I don't)".

Not quite; a dynamic_cast downcast requires a polymorphic type.

By that I infer you mean that A must have at least one virtual method?

Yes, that seems reasonable for dynamic_cast<> to work properly.

All this "just jam it in there" behavior with C-style casts is why I
consider them a code smell in C++ code.
 
S

Stuart Redmann

Seriously, I am telling the truth: I have Visual C++ 2008, and it compiles
and lets me to use it as normal parameter after that. If I call
((B*)a1.a)->var later on, it will give 77!

VisualC 6.0 gives you an Access Violation when you try access
((B*)a1.a)->var, most probably because VC6.0 puts the two local
variables a1 and a2 in a different order on the stack. If you exchange
the roles of a1 and a2, you can access ((B*)a1.a)->var without Access
Violations but you'll get a corrupted stack (which results in an
Access Violation upon exit of main).

Regards,
Stuart
 
C

crea

Stuart Redmann said:
VisualC 6.0 gives you an Access Violation when you try access
((B*)a1.a)->var, most probably because VC6.0 puts the two local
variables a1 and a2 in a different order on the stack. If you exchange
the roles of a1 and a2, you can access ((B*)a1.a)->var without Access
Violations but you'll get a corrupted stack (which results in an
Access Violation upon exit of main).

Yes you are right, I did not run the program to the end, just debugged
partly, so did not notice this.

It all makes sense...
 
G

Goran

I know its simple, but I cannot find the answer from books because its a
special case.
Here:
///////////////////////
class A
{
public:
 A* a;

};

class B : public A
{
public:
 int var;

};

A a1, a2;

a1.a = &a2;
((B*)a1.a)->var = 77;
////////////////
I compiled and it works.

It does not, it only looks as if it works. First off, if you debug
through this with visual studio, it should tell you that you corrupted
the stack. Did you try that? Also, try this:

1. add B() : var(1) {} to class B
2. try then this code:

if (((B*)a1.a)->var != 1)
cout << "UB baby!";

you will hit cout. If you really had B behind a1.a, how would that be
possible? Clearly B::var is 1, isn't it?

How/why can a1.a set B classes member variable's value even though there is
not even a one object created from B? How is it possible.

Because you lied to the compiler when you used the cast to B*. In this
case, cast amounted to static_cast (I think). With static_cast, it's
your duty to make sure that object you're casting __from__ indeed is
the of the type you're casting __to__. But a1.a is of type A*, not B*.
If you fail to observe this rule, it's your fault.
So all objects are from class A and there is no object created from B andwe
are still using B's members. I thought that this var-variable is not even
allocated from the memory because there is no B object created??

This is correct. var is not allocated because no B has been allocated.
You managed to write into memory that wasn't allocated for you, and
that happened because you lied when you used the cast. Don't lie.

Goran.

P.S. In general, C-style casts should not be used in C++. Use of
appropriate C++-style casts makes intention clearer (but using e.g.
static_cast in your case leads to same problem; lying doesn't pay off).
 
P

Paul

Paul said:
Basically you're dereferencing an uninitialised pointer.

B* b;
b->var=77;

It just happens that the pointer is a member of a class, and you've
type-casted it. The pointer could point to anything. °_°

Sorry I overlooked the a1.a = &a2; assginement , my bad.
 
M

MikeWhy

crea said:
Seriously, I am telling the truth: I have Visual C++ 2008, and it compiles
and lets me to use it as normal parameter after that. If I call
((B*)a1.a)->var later on, it will give 77!

Seriously, we're not the ones with the miscomprehension. Your code works,
insofar as it explicitly discards type checking with a C-style typecast. The
question is what happens if you change B as follows:

class B : public A
{
public:
char foo[1024];
int var;
};

If it still doesn't fail, make foo even larger. You should conclude from
this that only a fool subverts type checking. If you rewrite your code to
honor the type, ...

B* b = a1.a; // fails. A is not a B.
b->var = 77;

.... the compiler should complain appropriately. Ignore or subvert at your
own peril.
 

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

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top