Reputation: 293
So i have some code like this:
#include <iostream>
using namespace std;
class Base1 {};
class Base2 {};
class A
{
public:
A() {}
void foo(Base2* ptr) { cout << "This is A. B is at the address " << ptr << endl; }
};
A *global_a;
class B : public Base1, public Base2
{
public:
B() {}
void bar()
{
cout << "This is B. I am at the address " << this << endl;
global_a->foo(this);
}
};
int main()
{
global_a = new A();
B *b = new B();
b->bar();
system("pause");
return 0;
}
But this is the output i get after compiling with Visual Studio 2013:
This is B. I am at the address 0045F0B8
This is A. B is at the address 0045F0B9
Press any key to continue . . .
Can somebody please explain why the addresses are different?
Upvotes: 3
Views: 224
Reputation: 598001
B
derives from Base1
and Base2
, so it consists of all the data that Base1
and Base2
contain, and all of the data that B
adds on top of them.
B::bar()
is passing a pointer to the Base2
portion of itself to A::for()
, not the B
portion of itself. B::bar()
is printing the root address of the B
portion, whereas A::foo()
is printing the root address of the Base2
portion instead. You are passing around the same object, but they are different addresses within that object:
If B
does not add any new data, its base address might be the same as the root address of its nearest ancestor (due to empty base optimization):
Don't rely on that. A compiler might add padding between the classes, for instance:
Always treat the various sections as independent (because they logically are). Just because B
derives from Base2
does not guarantee that a B*
pointer and a Base2*
pointer, both pointing at the same object, will point at the same memory address.
If you have a Base2*
pointer and need to access its B
data, use dynamic_cast
(or static_cast
if you know for sure the object is a B
) to ensure a proper B*
pointer. You can downcast from B*
to Base2*
without casting (which is why B::bar()
is able to pass this
- a B*
- to A::foo()
when it is expecting a Base2*
as input). Given a B*
pointer, you can always access its Base2
data directly.
Upvotes: 5
Reputation: 119527
0x0045F0B8
is the address of the complete B
object. 0x0045F0B9
is the address of the Base2
subobject of the B
object.
In general the address of the complete object might not be the same as the address of a base class subobject. In your case the B
object is probably laid out as follows:
+---+-------+
| | Base1 | <-- 0x0045F0B8
| B |-------+
| | Base2 | <-- 0x0045F0B9
+---+-------+
Each base class occupies one byte and Base1
is laid out before Base2
. The pointer to the complete B
points to the beginning, which is at 0x0045F0B8
, but the pointer to the Base2
points to the address inside the complete B
object at which the Base2
subobject starts, which is 0x0045F0B9
.
However when I compile your program on my system using g++ 4.8, I get the same address printed in both lines. This is presumably because the implementation is allowed to allocate no space at all for empty base classes (the so-called empty base class optimization) and the two base class subobjects Base1
and Base2
are both located at the very beginning of the B
object, taking no space, and sharing their address with B
.
Upvotes: 8