views:

429

answers:

9

Hello

I want to create in C++ an array of Objects without using STL.

How can I do this?

How could I create array of Object2, which has no argumentless constructor (default constructor)?

+12  A: 

If the type in question has an no arguments constructor, use new[]:

Object2* newArray = new Object2[numberOfObjects];

don't forget to call delete[] when you no longer need the array:

delete[] newArray;

If it doesn't have such a constructor use operator new to allocate memory, then call constructors in-place:

//do for each object
::new( addressOfObject ) Object2( parameters );

Again, don't forget to deallocate the array when you no longer need it.

sharptooth
Object2 has no default c'tor.
KennyTM
you broke my brain with this construct: ::new( address ) Object2( parameters );
Andrey
@Andrey: That's plain old placement new.
sharptooth
//do for each object -- but how can I create an array of Object2
osgx
Placement new: http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10
Mark Ransom
@osgx: `addressOfObject = new char[n * sizeof(Object2)];`
Mark Ransom
@osgx: void* buffer = ::operator new[]( numberOfObjects * sizeof( Object2 )); That's not an array of objects, that's a block of memory.
sharptooth
I don't recommend using a placement new in this situation. It just obfuscates the purpose of the code, and as stated in the linked article, there are certain caveats to consider while using it.
Jeremy Bell
@Jeremy: What is the alternative? The objects do not have a default ctor, and he doesn't want to use `vector`
jalf
Placement new is absolutely the right answer. @sharptooth: it starts out as a block of raw memory, it becomes an array of objects when the constructors are called. But be sure to handle exceptions properly and call destructors manually and only on those objects which finished construction. It isn't trivial.
Ben Voigt
@jalf: There isn't an alternative that I know of that would do the exact same thing as declaring an array of Object2's. My suggestion sacrifices some memory overhead for a solution that is easier to read/understand/maintain. Though, given the fact that the op is specifically not using stl, then maybe performance/memory usage is more important in this case?
Jeremy Bell
@Jeremy Bell, I have to store objects in array, not with array of pointers. Array of pointers is easy and not so interesting as array of object. Second level of indirection is added, memory locality is destroyed, heap overhead can be big. And in Array I can store thousands of small objects.
osgx
@Jeremy: Whatever his reasons, we can either answer his question, or say "you could do something else instead". If we tell him to do something else instead, then the best suggestion would be "just use `std::vector`. If we do answer his exact question, then @sharptooth's answer is pretty much the only possible solution.
jalf
A: 

If no default constructor is available, you will need an array of pointers, and then loop over that array to initialize each of the pointers.

Space_C0wb0y
That's unlikely to be fast: allocation issues and cache misses are likely.
Matthieu M.
A: 
Object2 *myArray[42];
for (int i = 0; i < 42; i++)
{
  myArray[i] = new Object2(param1, param2, ...);
}

Later on you will have to walk through the array and deallocate each member individually:

for (int j = 0; j < 42; j++)
{
  delete myArray[j];
}
Raul Agrait
`Object2 myArray[42];` this step won't work unless `Object2` has a default constructor (user-declared or otherwise).
Charles Bailey
@Charles Bailey. Yup, I noticed that a second after I posted it, and changed it to an array of pointers.
Raul Agrait
heeey. It is not an array of objects, is it array of pointers!
osgx
A: 

The obvious question is why you don't want to use the STL.

Assuming you have a reason, you would create an array of objects with something like Obj * op = new Obj[4];. Just remember to get rid of it with delete [] op;.

You can't do that with an object with no constructor that doesn't take arguments. In that case, I think the best you could do is allocate some memory and use placement new. It isn't as straightforward as the other methods.

David Thornley
I'm trying to implement stl-like structures from scratch. It will be not a vector, but smth like B-tree of constant-length vectors. And I want full control, so I want to implement it entire by myself.
osgx
+2  A: 

Use an array of pointers to Object2:

std::tr1::shared_ptr<Object2>* newArray = new shared_ptr<Object2>[numberOfObjects];
for(int i = 0; i < numberOfObjects; i++)
{
    newArray[i] = shared_ptr<Object2>(new Object2(params));
}

Or, alternatively, without the use of shared_ptr:

Object2** newArray = new Object2*[numberOfObjects];
for(int i = 0; i < numberOfObjects; i++)
{
    newArray[i] = new Object2(params);
}
Jeremy Bell
As others point out, this adds a lot of memory usage overhead (uses a bunch of heap blocks, each of which costs a heap header) and breaks spatial locality.
Ben Voigt
@Ben Voigt, what do you mean "bunch of heap blocks"? How long is heap header and is it necessary for each `new Object2(params)`?
osgx
Size of heap header varies with compiler settings and whether the debug heap is in use or not, but for the implementations I'm familiar with it is a _minimum_ of 2*sizeof(void*), which is 8 bytes for a 32-bit process. Every call to ::new, ::new[], or malloc has to include space for the heap header inside the heap block it returns, and the return address is pretty much guaranteed to be a multiple of sizeof(void*). It would not be unusual for new char() to actually allocate 64 bytes or more in a debug build, which has checks for underflow, overflow, records line doing allocation, etc.
Ben Voigt
@Ben Voigt, I get it. Thanks!. What about next thing? With using pools of constant length allocations (e.g. `array_of_16byte_objects`), heap will increment counter of `objects_count_of_16byte`, and mark the place as filled in bitmap. Do you know any such implementation?
osgx
There are alternate allocators out there which use pools as you suggest (but what happens when the pool runs out, or the only "free" memory is in the pool and some other sized object needs to be allocated). And you can actually redefine "new" for your class to use the pool. But it's pointless to track usage of individual pool slots when you wanted a big array in the first place. Just using one big buffer along with placement new is more efficient than the pool. Now if you needed a linked list or something else that could grow, the pool might be better after all. But not for an array.
Ben Voigt
+3  A: 

You can do what std::vector does and create a block of raw memory. You then construct your objects which don't have a default constructor in that memory using placement new, as they are required. But of course, if you do that you might as well have used std::vector in the first place.

anon
+3  A: 

Assuming that your class is Base and you have a one argument constructor

Base arr[3] = {Base(0), Base(1), Base(2)} ;
hype
This is the best solution if you know the size of the array at compile time, if not go with vector really.
smerlin
I know the size at container consruction time only.
osgx
+3  A: 
// allocate memory
Object2* objArray = static_cast<Object2*>( ::operator new ( sizeof Object2 * NUM_OF_OBJS ) );
// invoke constuctors
for ( size_t i = 0; i < NUM_OF_OBJS; i++ )
  new (&objArray[i]) Object2( /* initializers */ );

// ... do some work

// invoke destructors
for ( size_t i = 0; i < NUM_OF_OBJS; i++ )
  objArray[i].~Object2();

// deallocate memory
::operator delete ( objArray );
Kirill V. Lyadvinsky
Does static_cast work like that? (I always figured you needed reinterpret_cast to get from void* to anything else, but honestly have never tried static_cast.)...and you forgot to destroy your objects before deleting the array, you need to explicitly call the dtor:for (...) objArray[i].~Object2();
dash-tom-bang
+1 /more to go/
osgx
Why we need here a static_cast not the simple `(Object2*)` one?
osgx
@osgx: Using C++ style casts rather than a C style cast means that you always get the type of conversion that you ask for. If you make an error then you get a copmile error rather than falling back to a potentially more dangerous conversion that a C style cast would give you.
Charles Bailey
@dash-tom-bang, thanx, I completely forgot about destructors :) `static_cast` is more safe here because it can't cast `Object2*` to `SomeThingElse*` where `reinterpret_cast` (or C-style cast) can.
Kirill V. Lyadvinsky
Explicitly calling destructors? Are you sure this isn't ... you know ... evil?
Chris Burt-Brown
No, that's ok in this case. Check this article for example: http://www.codeproject.com/KB/tips/Dynamic_array_template.aspx
Kirill V. Lyadvinsky
A: 

If you genuinely need an array (contiguous sequence of objects) of a non-default constructible type and for some reason you cannoy user std::vector (!?) then you have to use a raw allocation function and placement new.

This is very hard to do reliably; this should help to show why. This snippet includes some defence against exceptions but is more than likely not robust against all failures.

const size_t required_count = 100; //e.g.

// cast to pointer of required type needed for pointer arithmetic
Object2* objarray = static_cast<Object2*>(operator new(required_count * sizeof(Object2)));

size_t construction_count = 0;

try
{
    while (construction_count < required_count)
    {
        // params could change with index.
        new (static_cast<void*>(objarray + construction_count)) Object2(param1, param2);
        ++construction_count;
    }
}
catch (...)
{
    while (construction_count-- != 0)
    {
        try
        {
            (&objarray[construction_count])->~Object2();
        }
        catch (...)
        {
            // not a lot we can do here, log but don't re-throw.
        }
    }

    operator delete(objarray);
    throw;
}

// Now objarray has been allocated and pointer to an array of required_count Object2
// It cannot be de-allocated via delete[] or delete; you must loop through
// calling destructors and then call operator delete on the buffer.
Charles Bailey
There are many good reasons to avoid STL, and those who avoid STL also tend to avoid exceptions. ;)
dash-tom-bang
Can I do a construction of object without placement new, but using`objarray[i]->Object2(param1,param2)` ?
osgx
@dash-tom-bang: I'm attempting to be as exception neutral as possible, only protecting against exceptions thrown by memory allocation failure and the client object. I'm not throwing any new exceptions that other solutions aren't; they are (in general) just ignoring potential exceptions that I'm not.
Charles Bailey
@osgx: No. placement `new` is the only way to construct an object without also allocating memory in the same statement. You can't call a constructor on an existing object which is what your suggested syntax is implying.
Charles Bailey