views:

3968

answers:

5

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.

+4  A: 

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.

MadKeithV
There is no direct way; you can use std::vector as mentioned in one of the answers below.
Jaywalker
+3  A: 

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
Johannes Schaub - litb
Of course, in the std::vector case, the 'array' is in the stack but the objects are not.
neither the "array" nor the objects are on the stack actually if you use the standard allocator.
Johannes Schaub - litb
Actually some compilers (notably g++) do support stack arrays with runtime-determined size. This is apparently a transplanted C99 feature called Variable Length Arrays.
Alastair
yes, you are right. That c99 is supported by gcc. but C++ doesn't need it :)
Johannes Schaub - litb
A: 

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.

Steve Jessop
Wouldn't that initialize the vector with a single ArrayList shared by all entries in the vector ?
Leonel
no. the vector copies it
Johannes Schaub - litb
Steve Jessop
indeed, this doesn't allocate on the stack - unless the vector is provided another allocator.
xtofl
Yes, that's why I present the vector as an alternative to consider, rather than as a direct answer to the question. This isn't a quiz, after all, so it's often useful to offer ways to do things similar to what the question actually says, in the hope that it meets the real requirements.
Steve Jessop
A: 

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.

Nils Pipenbrinck
i would strongly discourage from that. the ode you used above is already done better by std::vector (using the heap instead of the unportable alloca).
Johannes Schaub - litb
I'd say it's worth knowing about, for that very rare situation where the heap is too slow and you know an upper bound on N and your own stack usage such that the array will fit. Not the go-to solution by any means, but it is what was asked for...
Steve Jessop
fair enough :) however he asked about a c++ solution. alloca is not even POSIX :)
Johannes Schaub - litb
I've used placement new once in my 10 years as a developer - where there was a statically allocated array that I couldn't touch (legacy library code) but I had to get an object in that had no default constructor. Nasty hack ;-)
MadKeithV
I have to agree, guys. I never do such hacks in production code either.
Nils Pipenbrinck
A: 

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.

xtofl