Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do objects that are within other objects really share the same address?

Tags:

c++

object

memory

I recently ran into this question while I was working on a project, and it confused me a bit. So I decided to write a test program to get a definitive answer:

#include <iostream>

using namespace std;

class layer3{
public:
    layer3(){}
    ~layer3(){}     
private:

};


class layer2{
public:
    layer2(){}
    ~layer2(){}

    layer3* GetBAddress(){return &b;}
private:
    layer3 b;
};


class layer1{
public:
    layer1(){}
    ~layer1(){}

    //returns the address of a, which is a 'layer2' object
    layer2* GetaAddress(){return &a;}
    //returns the address of b, which is is a layer 3 object
    layer3* GetDeepBAddress(){return a.GetBAddress();}
private:
    layer2 a;

};

int main(){

    layer1 t;
    cout << &t << "  : layer 1's address" << endl;
    cout << t.GetaAddress() <<  "  : layer 2's address" <<endl;
    cout << t.GetDeepTLAddress() <<  "  : layer 3's address" <<endl;

}

This program creates 3 objects. layer2 is created inside layer1, and layer3 is created inside layer2. Then I call to get the addresses of layer1, layer2, and layer3, and just as was happening before, this is the output:

$ ./a.exe
0x28ac4f  : layer 1's address
0x28ac4f  : layer 2's address
0x28ac4f  : layer 3's address

How can all three of these objects share the same spot in memory? What if I scaled this program to have 50 layers (objects)? Or 10,000? I'm not quite sure how this is possible. Can someone please put me in my place and explain what's going on here?

Edit: Perhaps it's because I instantiated the objects in private rather than in the objects' constructors? Baah I don't know.

like image 548
xcdemon05 Avatar asked Jan 23 '13 13:01

xcdemon05


2 Answers

The most definitive answer is that given by the C++ standard:

Two objects that are not bit-fields may have the same address if one is a subobject of the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses.

That is, if an object is a subject of another, they may have the same address.

In C++11, a standard-layout struct (despite its name, that can be a class too) object's first member is guaranteed to have the same address as the object itself:

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

Since your classes are all standard-layout, the behaviour you've observed is guaranteed by C++11.

In C++03, the rule is similar, but applies to POD-struct types, rather than standard-layout struct types. Your classes, however, are not POD-struct types because they have user-defined destructors. So the behaviour you see here is not guaranteed by C++03.

So why can this occur? Well all a class really is is a way to group some data together and provide operations on that data. Consider a class that just contains an int like so:

class A
{
  int x;
};

All this class is made up of is that int. When you create an object of type A, all you're really doing is allocating enough space for its innards and initialising them (or in this case, not initialising them). Let's say we create two instances of A:

A a1;
A a2;

What do we have in memory? You could imagine that it looks like this:

   a1     a2
┌──────┬──────┐┄┄
│  A   │  A   │
└──────┴──────┘┄┄
Memory ------->

If we know that A just contains an int - that is, an A object is really nothing more than an int (except for maybe some padding) - then we know that the memory actually looks something like this if we break it down a bit more:

   a1     a2
┌──────┬──────┐┄┄
│ int  │ int  │
└──────┴──────┘┄┄
Memory ------->

You can see here that the A and int would both have the same address because the int is a subobject of the objects of type A. If A contained both an int and a char, it might look something like this:

       a1            a2
┌──────┬──────┬──────┬──────┐┄┄
│ int  │ char │ int  │ char │
└──────┴──────┴──────┴──────┘┄┄
Memory ------->

We know that char will have a higher address than the int because, once again, the standard says so:

Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object.

Note that a subobject does not necessarily share its address with the object its contained within, even if it's the first one. It's entirely up to the compiler.

like image 200
Joseph Mansfield Avatar answered Nov 10 '22 10:11

Joseph Mansfield


In memory, an instance of a class (generally) occupies the space of its data members (plus a virtual table pointer or other implementation defined bits).

From a purely data member point of view, an instance of layer1 contains only an instance of layer2, so it's natural that the first element stored in layer1 (the one which has the same address as the layer1 instance itself) is the layer2 instance. The same reasoning applies to layer2 and layer3.

like image 44
Angew is no longer proud of SO Avatar answered Nov 10 '22 09:11

Angew is no longer proud of SO