Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Consider the following piece of Java code.

int N = 10;
Object obj[] = new Object[N];
for (int i = 0; i < N; i++) {
    int capacity = 1000 * i;
    obj[i] = new ArrayList(capacity);
}

Because in Java, all objects live on the Heap, the array does not contain the objects themselves, but references to the objects. Also, the array itself is also an object, thus it lives on the heap.

What is the equivalent in C++, but keeping the array and objects on the stack, to avoid as much as possible needing new and delete ?

Edit: changed the code to use a custom constructor.

like image 487
Leonel Avatar asked Nov 26 '08 12:11

Leonel


4 Answers

Simply declaring

Object array_of_objects[10];

in C++ creates 10 default-constructed objects of type Object on the stack.

If you want to use a non-default constructor, that's not so easy in C++. There might be a way with placement new but I couldn't tell you off the top of my head.

EDIT: Link to other question on StackOverflow How to use placement new for the array is explained in the answer to this question here on StackOverflow.

like image 166
Joris Timmermans Avatar answered Oct 25 '22 04:10

Joris Timmermans


In C++, it is not possible to have an array on the stack with a size determined at runtime. Here you use std::vector to do that:

int N = 10;
std::vector<Object> obj(N);
// non-default ctor: std::vector<Object> obj(N, Object(a1, a2));
// now they are all initialized and ready to be used

If the size is known at compile-time, you can just go ahead with a plain array:

int const N = 10;
Object obj[N];
// non-default ctor: Object obj[N] = 
//     { Object(a1, a2), Object(a2, a3), ... (up to N times) };
// now they are all initialized and ready to be used

If you are allowed to use boost, it is better to use boost::array , since it provides iterators like containers do, and you will be able to get its size using .size():

int const N = 10;
boost::array<Object, N> obj;
// non-default ctor: boost::array<Object, N> obj = 
//     { { Object(a1, a2), Object(a2, a3), ... (up to N times) } };
// now they are all initialized and ready to be used
like image 37
Johannes Schaub - litb Avatar answered Oct 25 '22 06:10

Johannes Schaub - litb


Allocation can be done 'statically' (size known at compile time) or 'dynamically' (size determined at run time).

Static allocation is the plain old

int myarray[10];

To allocate on the stack, you need the alloca allocation function, which essentially just increments the stack pointer. (or decrements... whatever). Deallocation is done automatically.

int* myarray = (int*) alloca( n*sizeof(int) );

So you can initialize an array on the stack like Nils showed.

std::vector can work on the stack if provided a stack-allocator (the second, cumbersome template argument of vector)

My guess is that Boost does just this.

like image 4
xtofl Avatar answered Oct 25 '22 05:10

xtofl


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.

like image 3
Steve Jessop Avatar answered Oct 25 '22 04:10

Steve Jessop