android927
android927

Reputation: 293

C++: Pointer contains different address after being passed

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

Answers (2)

Remy Lebeau
Remy Lebeau

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:

image

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):

enter image description here

Don't rely on that. A compiler might add padding between the classes, for instance:

image2

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

Brian Bi
Brian Bi

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

Related Questions