Creating Array of Objects on the Stack and Heap

Creating array of objects on the stack and heap

You can create an array of objects on the stack via:

myarray stackArray[100]; // 100 objects

And on the heap (or "freestore"):

myarray* heapArray = new myarray[100];
delete [] heapArray; // when you're done

But it's best not manage memory yourself. Instead, use a std::vector:

#include <vector>
std::vector<myarray> bestArray(100);

A vector is a dynamic array, which (by default) allocates elements from the heap.††


Because your class has no default constructor, to create it on the stack you need to let the compiler know what to pass into the constructor:

myarray stackArray[3] = { 1, 2, 3 };

Or with a vector:

// C++11:
std::vector<myarray> bestArray{ 1, 2, 3 };

// C++03:
std::vector<myarray> bestArray;
bestArray.push_back(myarray(1));
bestArray.push_back(myarray(2));
bestArray.push_back(myarray(3));

Of course, you could always give it a default constructor:

class myarray
{
int i;
public:
myarray(int a = 0) :
i(a)
{}
};

† For the pedants: C++ doesn't really have a "stack" or "heap"/"freestore". What we have is "automatic storage" and "dynamic storage" duration. In practice, this aligns itself with stack allocation and heap allocation.

†† If you want "dynamic" allocation from the stack, you'd need to define a max size (stack storage is known ahead of time), and then give vector a new allocator so it uses the stack instead.

C++: how to create an array of objects on the stack?

For an array of ArrayList objects:

ArrayList obj[10];

The objects will be default initialised, which is fine for user-defined types, but may not be what you want for builtin-types.

Consider also:

std::vector<ArrayList> obj(10, ArrayList());

This initialises the objects by copying whatever you pass as the second parameter. So they're all the same, but not necessarily default. And as litb points out, the "10" in the vector can be replaced by a non-constant expression, whereas the "10" in the array declaration can't.

This doesn't actually put the ArrayList objects on the stack, it puts all 10 in a single allocation from the heap. So there may very rarely be performance concerns, if you really can't afford a single allocation. However, the std::vector is on the stack and it deletes any heap objects it uses when it is destroyed. So for the purposes of making sure your resources are freed, the vector behaves "as though" it were all on the stack.

Note that mixing a container of Object, with ArrayList values, as you do in your example Java code, is fraught with peril in C++. Basically you can't do it, even if ArrayList extends Object, because the array would only contain the storage for 10 Objects, and ArrayList likely requires more bytes to store than Object. The result is that any ArrayList you try to copy into the array would get "sliced": only the initial part of its representation is put in the array.

If you want a container of a type saying that it contains Objects, but which actually contains ArrayLists, then you need a container of pointers. To get good resource-handling, this probably means you need a container of smart pointers.

Array of Objects Memory Allocation(Stack And Heap)

why the memory allocation is not the one I draw on the right,

It is on the right, but if you were to use the value

per

or

per[0]

that reference would be brought onto the stack.

Is an array of an object stored in stack or heap?

Try this code:

#include <iostream>
#include <type_traits>
#include <cassert>
struct A{
A() {
std::cout<<"Called A() for "<<std::hex<<this<<'\n';
}
};

struct B {
A arr[10];
B() {
std::cout<<"Called B() for "<<std::hex<<this<<'\n';
}
};

int main() {
B* ptr = new B;
static_assert(std::is_standard_layout<B>::value); //Sanity check for this example's case
std::cout<<"Got ptr = "<<std::hex<<ptr<<std::dec<<" + sizeof(B) = "<<sizeof(B)<<'\n';
delete ptr;
return 0;
}

Output is:

Called A() for 0x10d4eb0
Called A() for 0x10d4eb1
Called A() for 0x10d4eb2
Called A() for 0x10d4eb3
Called A() for 0x10d4eb4
Called A() for 0x10d4eb5
Called A() for 0x10d4eb6
Called A() for 0x10d4eb7
Called A() for 0x10d4eb8
Called A() for 0x10d4eb9
Called B() for 0x10d4eb0
Got ptr = 0x10d4eb0 + sizeof(B) = 10

If you see the code and the corresponding output, you can correlate that the object ptr of type B is allocated on the heap at 0x10d4eb0 and it has a size of 10 (corresponding to 10 bytes, 1 byte each for the object A). So the memory address from 0x10d4eb0 till 0x10d4eb9 is all on the heap. And if you see the address of the objects of A, they all fall in this address range. Hence, the objects of A do lie in the heap memory area.

Now coming to how to handle arr, one needs to call delete/delete[], as the case may be, only on objects that have been allocated using new/new[]. Since that's not the case for arr, one doesn't need to call delete[] on it.

Arrays, heap and stack and value types

Your array is allocated on the heap, and the ints are not boxed.

The source of your confusion is likely because people have said that reference types are allocated on the heap, and value types are allocated on the stack. This is not an entirely accurate representation.

All local variables and parameters are allocated on the stack. This includes both value types and reference types. The difference between the two is only what is stored in the variable. Unsurprisingly, for a value type, the value of the type is stored directly in the variable, and for a reference type, the value of the type is stored on the heap, and a reference to this value is what is stored in the variable.

The same holds for fields. When memory is allocated for an instance of an aggregate type (a class or a struct), it must include storage for each of its instance fields. For reference-type fields, this storage holds just a reference to the value, which would itself be allocated on the heap later. For value-type fields, this storage holds the actual value.

So, given the following types:

class RefType{
public int I;
public string S;
public long L;
}

struct ValType{
public int I;
public string S;
public long L;
}

The values of each of these types would require 16 bytes of memory (assuming a 32-bit word size). The field I in each case takes 4 bytes to store its value, the field S takes 4 bytes to store its reference, and the field L takes 8 bytes to store its value. So the memory for the value of both RefType and ValType looks like this:


0 ┌───────────────────┐
│ I │
4 ├───────────────────┤
│ S │
8 ├───────────────────┤
│ L │
│ │
16 └───────────────────┘

Now if you had three local variables in a function, of types RefType, ValType, and int[], like this:

RefType refType;
ValType valType;
int[] intArray;

then your stack might look like this:


0 ┌───────────────────┐
│ refType │
4 ├───────────────────┤
│ valType │
│ │
│ │
│ │
20 ├───────────────────┤
│ intArray │
24 └───────────────────┘

If you assigned values to these local variables, like so:

refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;

valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;

intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;

Then your stack might look something like this:


0 ┌───────────────────┐
│ 0x4A963B68 │ -- heap address of `refType`
4 ├───────────────────┤
│ 200 │ -- value of `valType.I`
│ 0x4A984C10 │ -- heap address of `valType.S`
│ 0x44556677 │ -- low 32-bits of `valType.L`
│ 0x00112233 │ -- high 32-bits of `valType.L`
20 ├───────────────────┤
│ 0x4AA4C288 │ -- heap address of `intArray`
24 └───────────────────┘

Memory at address 0x4A963B68 (value of refType) would be something like:


0 ┌───────────────────┐
│ 100 │ -- value of `refType.I`
4 ├───────────────────┤
│ 0x4A984D88 │ -- heap address of `refType.S`
8 ├───────────────────┤
│ 0x89ABCDEF │ -- low 32-bits of `refType.L`
│ 0x01234567 │ -- high 32-bits of `refType.L`
16 └───────────────────┘

Memory at address 0x4AA4C288 (value of intArray) would be something like:


0 ┌───────────────────┐
│ 4 │ -- length of array
4 ├───────────────────┤
│ 300 │ -- `intArray[0]`
8 ├───────────────────┤
│ 301 │ -- `intArray[1]`
12 ├───────────────────┤
│ 302 │ -- `intArray[2]`
16 ├───────────────────┤
│ 303 │ -- `intArray[3]`
20 └───────────────────┘

Now, if you passed intArray to another function, the value pushed onto the stack would be 0x4AA4C288, the address of the array, not a copy of the array.

Can I dynamically create instances of C++ objects in the stack?

Just as it's possible to create a new instance of an object in the heap using new Class(), is it possible to create an object in the stack?

Sure, simply declare it, without static, as a local variable inside of a function, eg:

void doSomething()
{
...
MyObject myObject; // <-- allocated on the stack
...
}

suppose that I have this:

static int emptyArray[100000];

there's sufficient RAM space, not allocated by the OS, for me to build my object. Can I use a custom allocator and build my object in a subset of this array?

You don't need a custom allocator, you can use placement-new for that, eg:

static int emptyArray[100000];
...
MyObject *myObject = new (&emptyArray[offset]) MyObject; // <-- allocated inside of emptyArray
...
myObject->~MyObject(); // <-- must call destructor manually

Just make sure that offset is an even multiple of MyObject's size and alignment. You can use std::aligned_storage to help you with that, eg:

#include <type_traits>

static const int MaxObjects = ...;
static std::aligned_storage<sizeof(MyObject), alignof(MyObject)>::type emptyArray[MaxObjects];
...
MyObject *myObject = new (&emptyArray[index]) MyObject;
...
myObject->~MyObject();


Related Topics



Leave a reply



Submit