Are Data Members Allocated in the Same Memory Space as Their Objects in C++

Are data members allocated in the same memory space as their objects in C++?

Each time you "instantiate" an object/symbol using a new (we are speaking C++ here), a new memory zone will be allocated for this object. If not, it will be put on the "local" memory zone.

The problem is that I have no standard definition for "local" memory zone.

An example

This means that, for example:

struct A
{
A()
{
c = new C() ;
}

B b ;
C * c ;
}

void doSomething()
{
A aa00 ;
A * aa01 = new A() ;
}

The object aa00 is allocated on the stack.

As aa00::b is allocated on a "local" memory according to aa00, aa00::b is allocated inside the memory range allocated by the new aa00 instruction. Thus, aa00::b is also allocated on stack.

But aa00::c is a pointer, allocated with new, so the object designed by aa00::c is on the heap.

Now, the tricky example: aa01 is allocated via a new, and as such, on the heap.

In that case, as aa01::b is allocated on a "local" memory according to aa01, aa01::b is allocated inside the memory range allocated by the new aa01 instruction. Thus, aa01::b is on the heap, "inside" the memory already allocated for aa01.

As aa01::c is a pointer, allocated with new, the object designed by aa01::c is on the heap, in another memory range than the one allocated for aa01.

Conclusion

So, the point of the game is:

1 - What's the "local" memory of the studied object: Stack of Heap?

2 - if the object is allocated through new, then it is outside this local memory, i.e., it is elsewhere on the heap

3 - if the object is allocated "without new", then it is inside the local memory.

4 - If the "local" memory is on the stack, then the object allocated without new is on the stack, too.

5 - If the "local" memory is on the heap, then the object allocated without new is on the heap, too, but still inside the local memory.

Sorry, I have no better vocabulary to express those concepts.

In C++, where in memory are class functions put?

It's not necessarily true that "each object - when created - will be given space in the HEAP for member variables". Each object you create will take some nonzero space somewhere for its member variables, but where is up to how you allocate the object itself. If the object has automatic (stack) allocation, so too will its data members. If the object is allocated on the free store (heap), so too will be its data members. After all, what is the allocation of an object other than that of its data members?

If a stack-allocated object contains a pointer or other type which is then used to allocate on the heap, that allocation will occur on the heap regardless of where the object itself was created.

For objects with virtual functions, each will have a vtable pointer allocated as if it were an explicitly-declared data member within the class.

As for member functions, the code for those is likely no different from free-function code in terms of where it goes in the executable image. After all, a member function is basically a free function with an implicit "this" pointer as its first argument.

Inheritance doesn't change much of anything.

I'm not sure what you mean about DLLs getting their own stack. A DLL is not a program, and should have no need for a stack (or heap), as objects it allocates are always allocated in the context of a program which has its own stack and heap. That there would be code (text) and data segments in a DLL does make sense, though I am not expert in the implementation of such things on Windows (which I assume you're using given your terminology).

Are the data members in Stack or Heap memory in C++

First of all: stacks and heaps are not C++ language concepts, but are implementation concepts. The C++ standard talks about automatic and dynamic storage instead. But for simplicity lets just talk about stacks and heaps.

Typically the new operator will put your object on the heap (unless new operator is overloaded). Otherwise your object goes to the stack.

  1. is fook2->c on the stack mem (the obj is on the heap mem)

The c part of a Fook object will be in the same place where the object is. In the fook2 case it is the heap. Note the subtlety. You said fook2->c which is the arrow operator: it actually loads c (unless overloaded). So the result lands to wherever the left side of fook2->c is.


  1. is *arr in idk1 on the heap mem (the obj is on the stack mem)

Again: arr field on an IDK object lives wherever the object lives. In the case of idk1 we have that arr pointer lives on stack. However the thing it is pointing to, i.e. *arr lives on the heap (since new was used). There's a subtlety here as well: * operator actually loads data to wherever the left side is (again: unless overloaded).


  1. is *arr in idk2 on the heap mem

idk2 lives on the heap, and thus arr pointer lives on the heap. Additionally *arr lives on the heap since that's how it was declared via new operator.


  1. is *fook4 in ctor of idk1 on the heap mem

fook4 pointer lives on the stack. The data it is pointing to lives on the heap.


  1. is fook3 in ctor of idk2 on the heap mem

fook3 object lives on the stack. Always. It doesn't matter that the constructor was called during new initialization of the heap object idk2. Constructor is just a function, a bit special, but not really different from other functions. The constructor doesn't even know whether it is called on a heap or stack object, and it doesn't care.


  1. is *fook4 in ctor of idk2 on the heap mem

Similarly to (3) and (4) fook4 pointer lives on the stack, while the data it is pointing to (i.e. *fook4) lives on the heap.

Class members and explicit stack/heap allocation

I think that you are confusing "stack/heap allocation" and "automatic variable".

Automatic variables are automatically destroyed when going out of context.

Stack allocation is the fact that the memory is allocated on the execution stack. And variable allocated on the stack are automatic variables.

Also, members are automatic variables whose destructors get called when its owner is destroyed. In the case of pointers, they are destroyed but not the underlying object, you have to explicitly call delete. To make sure that the underlying object is destroyed you have to use smart or unique pointers.

To put it another way: variables/members that you have to call delete on, are not automatic variables.

Lastly, member of a class are allocated on the same memory segment of the its owner.

In you code:

  • A.m_B is an automatic variable. If A is on the stack so is B and if A is on the heap so is B.
  • B.m_i and D.m_i are an automatic variables and will be allocated on the same memory segment of their owner
  • The pointer C.m_D is an automatic variable, but the pointed object of type D is not, you have to explicitly call delete on the pointer to delete the underlying object. So, the pointer C.m_D is allocated on the same memory segment, but not the underlying objet. It's cleary allocated by new and will be on the heap.

So:

  • Case 1: Everything is on the stack and automatic (ie: destroyed automatically).
  • Case 2: myA2 is on the heap and not automatic (you have to delete myA2). Its member m_B2 is an automatic variable that will be destroyed when myA2 is destroyed. Also since myA2 is on the heap, m_B, like any member of a class, is in the same memory space the heap too.
  • Case 3: myC1 is on the stack and is an automatic variable, The pointer to m_D is on the stack too, but not the object pointed by m_D which is allocated by new on the heap.
  • Case 4: Same as case3 but myC2 is on the heap and is not automatic. So you have to delete myC2 (which will delete m_D).

Memory allocation for member functions in C++

For each instance of the class, memory is allocated to only its member variables i.e. each instance of the class doesn't get it's own copy of the member function. All instances share the same member function code. You can imagine it as compiler passing a hidden this pointer for each member function so that it operates on the correct object. In your case, since C++ standard explictly prohibits 0 sized objects, class A and class B have the minimum possible size of 1. In case of class C since there is a virtual function each instance of the class C will have a pointer to its v-table (this is compiler specific though). So the sizeof this class will be sizeof(pointer).

C++ Contiguous memory access for object data members

  • The standard guarantees that they will be in that order in memory

    (if you took their addresses they would increment).

  • It does not guarantee that they will be in contiguous memory; but if that is the most optimal layout then they probably will be. Compiler is allowed to add padding between members (it usually does this to make access more efficient (sacrifice space for speed)). If all the members are the same size this is unlikely.

Note: Introducing public/private/protected between them complicates things and may change the order.

Are you better of using an array?

That depends. Would you normally accesses them via an index or via the name? I would say thay 99% of the time the first version you have is better but I can imagine use cases where std::array<> could be useful (members accessed via an index).

It was suggested in the comments std::vector<> could also be used. This is true and the standard guarantees the members are in contiguous location, but they may not be local to the object (in a std::array<> they are local to the object).

Usage of objects or pointers to objects as class members and memory allocation

Say I have an object that contains three stl vectors. Is my thinking correct that if they are regular members of the class, the memory of them will be "together" for the whole object? e.g. my memory would look sth like 10 blocks vector A, 5 blocks vector B, and then 15 blocks vector C.

Each vector occupies a fixed size in the containing object, independent of the number of elements currently stored by the vector. It is likely that the value_type of the vector (e.g. vector<int> has value_type int) won't affect the size of the contained vector object itself: only the amount of heap-allocated store the vector needs in order to maintain its storage capacity (so they're likey to be say 8 or 16 or 32 bytes each but all the same, but not 10 "blocks" (whatever that might be), 5 blocks and 15).

Would then once I insert more objects into vector A so that the space runs out the whole structure including vector B and C need to be moved?

Inserting elements into A can only ever cause existing elements in A to be moved (when the capacity is exceeded). B and C can never be affected.

Is that then an argument for pointers? Or are vectors internally only a pointer to the allocated memory?

YES, it's an argument... such a good one that YES, vectors already do use pointers to the contiguous memory where the actual value_type elements are stored.

Same question would go for lists etc...

YES, lists store their value_type elements on the heap too, and the size of the object embedding or derived from list is unaffected by operations on the list.

Are there any rules of thumb as to the cost of a redirection vs the cost of copying small objects? Maybe sth along the lines of 5 pointer redirection = 1 integer copy?enter code here

C++ runs on too many platforms for there to be good rules of thumb for this. Even on say x86 processors, differences in instruction set, # cores, cache sizes, CPU vendor / model / generation etc. can be overwhelming. Indirection is most costly if it results in memory page faults, and that's very dependent on the overall picture of program execution on the machine. If you care, benchmark real computers running the program until you find statistically relevant and stable results.



Related Topics



Leave a reply



Submit