Leonel
Leonel

Reputation: 29219

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.

Upvotes: 5

Views: 25755

Answers (6)

Elliott
Elliott

Reputation: 1376

If you happen to use Qt, you can use the QVarLengthArray

It takes a size as a second template parameter, and it will statically allocate an array with that size, and use that as the backing for the array instead of the heap like a std::vector or QVector does. If you add more than the template-specified size, it'll use heap allocation instead.

Example:

//the following ints will all be stored on the stack,
//and a heap allocation is never performed to store the array
QVarLengthArray<int, 10> objArray;
for (int i = 0; i < 8; i++) {
    int capacity = 1000 * i;
    objArray.push_back(capacity);
}

//since it's a class and not a raw array, we can get the array's size
std::cout << objArray.size(); //result is 8

//a heap allocation will be performed if we add an eleventh item,
//since the template parameter of 10 says to only statically allocate 10 items
objArray.push_back(0); //9 items
objArray.push_back(0); //10 items
objArray.push_back(0); //11 items - heap allocation is performed

If you stay below the template parameter size, you'll avoid the performance hit of a heap allocation - you'll effectively have a dynamically allocated stack-based array. The only disadvantage is that it's wasteful of memory if you don't use exactly as many items as the template parameter specifies: If you use too few, then the empty spaces are wasted. if you use too many, then the entire stack allocated area is wasted.

Sometimes trading performance for memory is worth it, and sometimes it isn't. I would recommend against blindly using this class - only use it if you know via profiling that the heap allocation of std::vector is one of your program's bottlenecks.

Upvotes: 0

Steve Jessop
Steve Jessop

Reputation: 279255

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.

Upvotes: 3

xtofl
xtofl

Reputation: 41509

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.

Upvotes: 4

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506925

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

Upvotes: 4

Joris Timmermans
Joris Timmermans

Reputation: 10978

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.

Upvotes: 5

Nils Pipenbrinck
Nils Pipenbrinck

Reputation: 86333

You can even allocate variable numbers of objects on the stack. You have to mix C and C++ to do so though.

// allocate storage for N objects on the stack
// you may have to call _alloca and include something to use this.
object * data = (object *) alloca (N * sizeof (object));

// initialize via placement new.
for (int i=0; i<N; i++)
  new (&data[i])();

Code is untested, but in principle it works that way.

Upvotes: 2

Related Questions