Question regarding cast

S

somenath

I have come to know from C++ book that "a single object (e.g an object of type derived) might have some more than one address (e.g its address when pointed to by Base * pointer and its address when pointed to by a Derived* pointer). That cannot happen in C"
So to understand this concept I wrote the following program

#include<iostream>
using namespace std;



class Base { };
class Derived:public Base
{
};
int main (void) {
Derived d;
Derived *d1;

Base *bp = &d;
d1= &d;
cout<<"bp = "<<bp<<endl;
cout<<"&d = "<<&d<<endl;
cout<<"d1 = "<<d1<<endl;
return 0;
}
===
Output
bp = 0x28ac57
&d = 0x28ac57
d1 = 0x28ac57

====
My understanding from the above mentioned text is as follows.
The object "d" is pointed by bp (base class pointer) and derived class pointer d1.
I was expecting value of d1 and bp will be different as they are of type derived and base class.
I think my understanding about the above mentioned the text is wrong.
Please help me to understand the following concept
"a single object (e.g an object of type derived) might have some more than one address (e.g its address when pointed to by Base * pointer and its address when pointed to by a Derived* pointer). That cannot happen in C".

How a single object can have multiple address?

Along with this I am interested to know how the C++ compiler restrict the access of function defined only in derived class from a base class pointer.
For example in the following code

class Base { };
class Derived:public Base
{
public:
void print()
{
cout<<"Inside derived "<<endl;
}
};
int main (void) {
Derived d;
Base *bp = &d;
bp->print();
return 0;
}

how the compiler makes sure that "bp" can not access "print" ?
Could you please provide me some resources which explain this topics?
 
I

Ike Naar

I have come to know from C++ book that "a single object (e.g an
object of type derived) might have some more than one address (e.g its
address when pointed to by Base * pointer and its address when pointed
to by a Derived* pointer). That cannot happen in C"
So to understand this concept I wrote the following program

#include<iostream>
using namespace std;



class Base { };
class Derived:public Base
{
};
int main (void) {
Derived d;
Derived *d1;

Base *bp = &d;
d1= &d;
cout<<"bp = "<<bp<<endl;
cout<<"&d = "<<&d<<endl;
cout<<"d1 = "<<d1<<endl;
return 0;
}
===
Output
bp = 0x28ac57
&d = 0x28ac57
d1 = 0x28ac57

Try this:

/* begin code */
#include <iostream>
class Base1 { char i; };
class Base2 { char j; };
class Derived : public Base1, public Base2 {};
int main()
{
Derived d;
Base1 *b1 = &d;
Base2 *b2 = &d;
std::cout << "&d=" << &d << " b1=" << b1 << " b2=" << b2 << "\n";
return 0;
}
/* end code */

/* begin output /*
&d=0x7f7fffffdb40 b1=0x7f7fffffdb40 b2=0x7f7fffffdb41
/* end output */
Along with this I am interested to know how the C++ compiler restrict
the access of function defined only in derived class from a base class
pointer.
For example in the following code

class Base { };
class Derived:public Base
{
public:
void print()
{
cout<<"Inside derived "<<endl;
}
};
int main (void) {
Derived d;
Base *bp = &d;
bp->print();
return 0;
}

how the compiler makes sure that "bp" can not access "print" ?

bp has type "pointer to Base" and Base has no member called "print",
so bp->print() makes no sense.
You could add such a member to the definition of Base:

class Base
{
public:
virtual void print()
{
std::cout << "Inside base\n";
}
};

Now class Base has a "print" member function; this function is
overridden in class Derived, so when you call

bp->print();

while bp points to a Derived object, it will print "Inside derived".
Could you please provide me some resources which explain this topics?

Any good book on C++.
For online material, see e.g. www.cplusplus.com, in particular

http://www.cplusplus.com/doc/tutorial/polymorphism

or search for Bruce Eckel's (free) online book "Thinking in C++".
 
J

Jorgen Grahn

I have come to know from C++ book that "a single object (e.g an
object of type derived) might have some more than one address (e.g its
address when pointed to by Base * pointer and its address when pointed
to by a Derived* pointer). That cannot happen in C"

Of course not -- there is no inheritance in C! Which book is this?

Perhaps it's an important point to make; I'm unsure. Are you likely
to run into problems because of it, I wonder?

Derived d;
std::vector<Base*> v;
v.push_back(&d);

std::find(v.begin(), v.end(), &d) ...

Surely a conversion from Derived* to Base* is happening here, so that
&d will be found?

/Jorgen
 
S

somenath

Try this:



/* begin code */

#include <iostream>

class Base1 { char i; };

class Base2 { char j; };

class Derived : public Base1, public Base2 {};

int main()

{

Derived d;

Base1 *b1 = &d;

Base2 *b2 = &d;

std::cout << "&d=" << &d << " b1=" << b1 << " b2=" << b2 << "\n";

return 0;

}

/* end code */



/* begin output /*

&d=0x7f7fffffdb40 b1=0x7f7fffffdb40 b2=0x7f7fffffdb41

/* end output */
But I am not getting why it needs to be different?
 
S

somenath

The text doesn't say that the pointers will *always* be different. It says

that they *can* be different.



They will be different in cases of multiple inheritance. They can also be

different if the base class has no virtual functions but the derived

class does.



--- news://freenews.netfront.net/ - complaints: (e-mail address removed) ---
But why it is different? What is achieved by having different address of same object? Could you please explain how compiler does it?
 
S

SG

Am 19.06.2013 10:58, schrieb somenath:
But I am not getting why it needs to be different?

Think about the memory layout. In this case, we obviously have
something like this:

Derived object
|
V
,-- --.
0x7f7fffffdb40 | i | <-- Base1 object
| --´
| --.
0x7f7fffffdb41 | j | <-- Base2 object
`-- --'

where the address you see is the object's starting address.

What you should keep in mind is the following: If you have a Base1 or
Base2 pointer and you know already that it points to a subobject of a
Derived object, you need a static_cast to get to a Derived pointer
because a static cast will include a pointer adjustment if necessary:

Derived d;
Base1* p = &d;
Base2* q = &d;
Derived* x = static_cast<Derived*>(p);
Derived* y = static_cast<Derived*>(q);
assert(&d == x);
assert(&d == y);

(A reinterpret_cast would not work here)

Of course, if your classes are polymorphic, then you could also use a
dynamic_cast for this.

Cheers!
SG
 
S

somenath

Am 19.06.2013 10:58, schrieb somenath:





Think about the memory layout. In this case, we obviously have

something like this:



Derived object

|

V

,-- --.

0x7f7fffffdb40 | i | <-- Base1 object

| --´

| --.

0x7f7fffffdb41 | j | <-- Base2 object

`-- --'



where the address you see is the object's starting address.



What you should keep in mind is the following: If you have a Base1 or

Base2 pointer and you know already that it points to a subobject of a

Derived object, you need a static_cast to get to a Derived pointer

because a static cast will include a pointer adjustment if necessary:



Derived d;

Base1* p = &d;

Base2* q = &d;

Derived* x = static_cast<Derived*>(p);

Derived* y = static_cast<Derived*>(q);

assert(&d == x);

assert(&d == y);



(A reinterpret_cast would not work here)



Of course, if your classes are polymorphic, then you could also use a

dynamic_cast for this.
I got it. Is there any book available that explains how these kinds of things are implemented in details?
Is there any literature which talks about the memory model of C++ in details?
 
I

Ike Naar

I got it. Is there any book available that explains how these kinds of
things are implemented in details?
Is there any literature which talks about the memory model of C++ in
details?

There's the book "Inside the C++ Object Model" by Stanley Lippman.

Be aware that details may be implementation-dependent; the
memory layout shown above is only one of many possible layouts.
Another implementation might lay out the memory for a Derived object
as, say,

Derived object
|
V
,-- --.
0x7f7fffffdb40 | j | <-- Base2 object
| --.
0x7f7fffffdb41 | ? | <-- seven bytes padding
| --.
0x7f7fffffdb48 | i | <-- Base1 object
`-- --'

Relying on implementation-dependent details can make your code
non-portable.
 
N

Nobody

But why it is different? What is achieved by having different address of
same object? Could you please explain how compiler does it?

Suppose you have a base class:

class Base {
int a, b;
};

then you create a derived class:

class Derived : public Base {
int c, d;
};

The compiler will typically implement this with the layout:

struct Derived {
Base _base;
int c, d;
};

So a pointer to a Derived automatically points to a Base. Something which
expects a pointer to a Base will ignore the extra members which follow the
Base in memory.

But suppose that the derived class has multiple bases:

class Base1 {
int a, b;
};

class Base2 {
float x;
};

class Derived : public Base1, public Base2 {
int c, d;
};

The layout might look like:

struct Derived {
Base1 _base1;
Base2 _base2;
int c, d;
};

A pointer to a Derived automatically points to a Base1, not to a Base2.

If you convert the pointer to a Base2* via static_cast, dynamic_cast or a
C-style cast, the compiler will adjust the pointer so that it points to
Derived::_base2, ensuring that it actually points to a Base2.

But if you convert the pointer via reinterpret_cast (or cast it from
Derived* to void* then to Base2*), no adjustment will be performed and
anything expecting a Base2* will get confused.
 
J

Jorgen Grahn

....

The layout might look like:

struct Derived {
Base1 _base1;
Base2 _base2;
int c, d;
};

A pointer to a Derived automatically points to a Base1, not to a Base2. ....
But if you convert the pointer via reinterpret_cast (or cast it from
Derived* to void* then to Base2*), no adjustment will be performed and
anything expecting a Base2* will get confused.

I.e. the lesson is not really

"You need to understand how C++ maps to a memory layout in a
typical implementation."

but rather

"Trust the normal C++ type system. Don't try to outsmart it using
a false mental model of the memory model mapping. Especially:
don't assume you can just reuse your mental model of some C
implementation."

Hopefully that was what the book (Accelerated C++, was it?) was trying
to get across.

As I understand this thread, none of this is a problem unless you
override the type system using reinterpret_cast<T> or something even
worse. And few people ever have to do that, to types like these,
anyway.

IMO it's a parallel to the endless endianness/padding/alignment etc
discussions. You can spend a lifetime fighting that stuff ... but it's
a non-issue unless you break the type system. Willingly and on purpose.

/Jorgen
 
N

Nobody

As I understand this thread, none of this is a problem unless you override
the type system using reinterpret_cast<T> or something even worse. And
few people ever have to do that, to types like these, anyway.

This is an issue for interfacing C++ code to C, or to other languages
which have mechanisms for interfacing with C.

Consider:

class SomeClass {...};

// C API
extern "C" {
void *new_object() { return static_cast<void*>(new SomeClass()); }
void free_object(void *obj) { delete static_cast<SomeClass*>(obj); }
void object_foo(void *obj) { static_cast<SomeClass*>(obj)->foo(); }
void object_bar(void *obj) { static_cast<SomeClass*>(obj)->bar(); }
// ... and so on
}

If you try to export the "an instance of a derived class is also an
instance of a base class" semantics to C, you'll get bitten by multiple
inheritance. You have to convert to the appropriate base class before
passing the pointer off to C code.
IMO it's a parallel to the endless endianness/padding/alignment etc
discussions. You can spend a lifetime fighting that stuff ... but it's a
non-issue unless you break the type system. Willingly and on purpose.

Again, this can be necessary if you have to interface to the outside
world, particularly if efficiency is a consideration.
 
J

James Kanze

This is an issue for interfacing C++ code to C, or to other languages
which have mechanisms for interfacing with C.

Since C doesn't have derived classes, I don't see how this can
be an issue. Classes which can be exported to C can't be
derived classes.
Consider:
class SomeClass {...};
// C API
extern "C" {
void *new_object() { return static_cast<void*>(new SomeClass()); }
void free_object(void *obj) { delete static_cast<SomeClass*>(obj); }
void object_foo(void *obj) { static_cast<SomeClass*>(obj)->foo(); }
void object_bar(void *obj) { static_cast<SomeClass*>(obj)->bar(); }
// ... and so on

}
If you try to export the "an instance of a derived class is also an
instance of a base class" semantics to C, you'll get bitten by multiple
inheritance. You have to convert to the appropriate base class before
passing the pointer off to C code.
Obviously.
Again, this can be necessary if you have to interface to the outside
world, particularly if efficiency is a consideration.

Again, it's not an issue *if* you respect the type system. One
of the rules of the types system is that to use a pointer
converted to void*, you have to convert it back to its original
type. Anything else is undefined behavior.
 
J

Jorgen Grahn

Again, this can be necessary if you have to interface to the outside
world, particularly if efficiency is a consideration.

Hm ... I'd like to see proof that proper machine-neutral encoding is a
bottleneck before trying dangerous, non-portable shortcuts. Do you
have real examples where it's worth it?

I have plenty of examples where it wasn't worth it; where it was just
laziness -- laziness which generated a lot of work for me, to clean up
the mess afterwards.

/Jorgen
 

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
474,039
Messages
2,570,375
Members
47,021
Latest member
AleciaMcMa

Latest Threads

Top