Class Members and Explicit Stack/Heap Allocation

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

Class members allocation on heap/stack?

Yes, yes, and yes.

Your first example has a bit of a bug in it, though: which is that because it one of its data members is a pointer with heap-allocated data, then it should also declare a copy-constructor and assignment operator, for example like ...

MyClass(const MyClass& rhs) 
{
MyMember = new char[250];
memcpy(MyMember, rhs.MyMember, 250);
}

Will heap allocated objects have their members allocated on the stack?

A subobject has the same storage duration as the complete object it is a part of. If an instance of DynamiclyManagedObject is dynamically allocated, then the StaticlyManagedObject member will be destroyed when the DynamiclyManagedObject is destroyed.

Informally, you might say that the subobject will be on the heap if and only if the complete object is on the heap. However, storage duration is the technically correct way to talk about it; heap and stack are implementation details.

Stack members Vs heap members in C++ objects

int m_int; // primitive type. hardly ever see a pointer

If I saw int *m my first reaction would be that it is an array of ints.

// For class instances, you always* seem to see this
SomeOtherClass* m_some_other_class_instance_1;
// and not this
SomeOtherClass m_some_other_class_instance_2;

You might want to heap allocate your object if you need to delay its loading, and not have it constructed when the outer class is. Another reason you might do it is for polymorphic reasons. If SomeOtherClass is base class in your constructor you might initialise a different child class. if(some_condition) m_ptr = new Child1(); else m_ptr = new Child2();
Again you might want to wrap this in a unique_ptr so the destruction is automatic and you don't leak.

 // But lately, I've noticed that for std:: templates, it doesn't seem to be this
vector<double>* m_vector_instance_1;
// but rather this
vector<double> m_vector_instance_2;

If you are holding on to a pointer of vector that isn't owned by this class a ptr isn't unexpected.

Heap allocating a vector (or other stl containers) doesn't make sense, partly because you are using them to take the pain out of dealing with c-style arrays, and managing the memory. Underneath a vector it will be heap allocated.

  SomeClass* some_class_instance_1 = new SomeClass();
// SomeClass instance on heap
// So all its members (both <xx>_1 and <xx>_2) are on heap as well
// Hence all its members will stay alive beyond the scope of this function (or do they?)

Yes, all its stack members will stay alive until its deleted. Any heap allocated ones must be destroyed as well, if you are doing manual allocation the be sure to call the delete, but best to wrap around something like a unique_ptr or shared_ptr1

  SomeClass some_class_instance_2;
// SomeClass instance on stack
// So the only piece of data relating to SomeClass that's on the heap is what's pointed to by <xx>_1 members
// But everything else will still stay alive within the scope of this function

This object and it members will get destroyed when it goes out of scope. Not though some of its members are pointers. Should they be pointing to a heap allocated member that has no other pointer you have a memory leak.

  // In conclusion, using either case above, members of a SomeClass instance stay alive for their intended period
// So are <xx>_1 members overkill?

I can't think of a valid reason to heap allocate an stl container in the context above. Heap allocating other members might be required, but prefer not to when you can, and even then prefer using smart_ptrs when you can.

  // Ah, ha, ha, ha, stayin' alive, stayin' alive ...

This is C++, you will die a slow and painful death.

Are the members of a heap allocated class automatically allocated in the heap?

It is important to understand that C++ uses "copy semantic". This means that variables and structure fields do not contain references to values, but the values themselves.

When you declare

struct A
{
int x;
double yarr[20];
};

each A you will create will contain an integer and an array of 20 doubles... for example its size in bytes will be sizeof(int)+20*sizeof(double) and possibly more for alignment reasons.

When you allocate an object A on the heap all those bytes will be in the heap, when you create an instace of A on the stack then all of those bytes will be on the stack.

Of course a structure can contain also a pointer to something else, and in this case the pointed memory may be somewhere else... for example:

struct B
{
int x;
double *yarr;
};

In this case the structure B contains an integer and a pointer to an array of doubles and the size in memory for B is sizeof(int)+sizeof(double *) and possibly a little more.
When you allocate an instance of B the constructor will decide where the memory pointed by yarr is going to be allocated from.

The standard class std::vector for example is quite small (normally just three pointers), and keeps all the elements on the heap.

What and where are the stack and heap?

The stack is the memory set aside as scratch space for a thread of execution. When a function is called, a block is reserved on the top of the stack for local variables and some bookkeeping data. When that function returns, the block becomes unused and can be used the next time a function is called. The stack is always reserved in a LIFO (last in first out) order; the most recently reserved block is always the next block to be freed. This makes it really simple to keep track of the stack; freeing a block from the stack is nothing more than adjusting one pointer.

The heap is memory set aside for dynamic allocation. Unlike the stack, there's no enforced pattern to the allocation and deallocation of blocks from the heap; you can allocate a block at any time and free it at any time. This makes it much more complex to keep track of which parts of the heap are allocated or free at any given time; there are many custom heap allocators available to tune heap performance for different usage patterns.

Each thread gets a stack, while there's typically only one heap for the application (although it isn't uncommon to have multiple heaps for different types of allocation).

To answer your questions directly:

To what extent are they controlled by the OS or language runtime?

The OS allocates the stack for each system-level thread when the thread is created. Typically the OS is called by the language runtime to allocate the heap for the application.

What is their scope?

The stack is attached to a thread, so when the thread exits the stack is reclaimed. The heap is typically allocated at application startup by the runtime, and is reclaimed when the application (technically process) exits.

What determines the size of each of them?

The size of the stack is set when a thread is created. The size of the heap is set on application startup, but can grow as space is needed (the allocator requests more memory from the operating system).

What makes one faster?

The stack is faster because the access pattern makes it trivial to allocate and deallocate memory from it (a pointer/integer is simply incremented or decremented), while the heap has much more complex bookkeeping involved in an allocation or deallocation. Also, each byte in the stack tends to be reused very frequently which means it tends to be mapped to the processor's cache, making it very fast. Another performance hit for the heap is that the heap, being mostly a global resource, typically has to be multi-threading safe, i.e. each allocation and deallocation needs to be - typically - synchronized with "all" other heap accesses in the program.

A clear demonstration:
Sample Image

Image source: vikashazrati.wordpress.com



Related Topics



Leave a reply



Submit